Merge "Implement Data.CARRIER_PRESENCE" into mnc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 347abb3..80bd125 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -10,16 +10,9 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.PROCESS_PHONE_ACCOUNT_REGISTRATION" />
-    <uses-permission android:name="android.permission.READ_CONTACTS" />
-    <uses-permission android:name="android.permission.READ_PROFILE" />
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
     <uses-permission android:name="android.permission.SEND_CALL_LOG_CHANGE" />
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
-    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
-    <uses-permission android:name="android.permission.WRITE_PROFILE" />
-    <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
-    <uses-permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL" />
-    <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" />
 
     <permission
             android:name="android.permission.SEND_CALL_LOG_CHANGE"
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index 76481bd..b5dbd06 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -515,7 +515,7 @@
                     if (tm != null) {
 
                         PhoneAccount account = tm.getPhoneAccount(handle);
-                        if (account != null) {
+                        if (account != null && account.getAddress() != null) {
                             // We did not find any items for the specific phone account, so run the
                             // query based on the phone number instead.
                             mDbHelper.getWritableDatabase().execSQL(UNHIDE_BY_ADDRESS_QUERY,
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 5cbca9e..8fce6a6 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -159,6 +159,7 @@
 import com.android.providers.contacts.MetadataEntryParser.RawContactInfo;
 import com.android.providers.contacts.MetadataEntryParser.UsageStats;
 import com.android.providers.contacts.util.Clock;
+import com.android.providers.contacts.util.ContactsPermissions;
 import com.android.providers.contacts.util.DbQueryUtils;
 import com.android.providers.contacts.util.NeededForTesting;
 import com.android.providers.contacts.util.UserUtils;
@@ -2214,7 +2215,7 @@
             response.putParcelable(Authorization.KEY_AUTHORIZED_URI, authUri);
             return response;
         } else if (PinnedPositions.UNDEMOTE_METHOD.equals(method)) {
-            getContext().enforceCallingOrSelfPermission(WRITE_PERMISSION, null);
+            ContactsPermissions.enforceCallingOrSelfPermission(getContext(), WRITE_PERMISSION);
             final long id;
             try {
                 id = Long.valueOf(arg);
@@ -2970,8 +2971,8 @@
     private void enforceSocialStreamReadPermission(Uri uri) {
         if (SOCIAL_STREAM_URIS.contains(sUriMatcher.match(uri))
                 && !isValidPreAuthorizedUri(uri)) {
-            getContext().enforceCallingOrSelfPermission(
-                    "android.permission.READ_SOCIAL_STREAM", null);
+            ContactsPermissions.enforceCallingOrSelfPermission(getContext(),
+                    "android.permission.READ_SOCIAL_STREAM");
         }
     }
 
@@ -2982,8 +2983,8 @@
      */
     private void enforceSocialStreamWritePermission(Uri uri) {
         if (SOCIAL_STREAM_URIS.contains(sUriMatcher.match(uri))) {
-            getContext().enforceCallingOrSelfPermission(
-                    "android.permission.WRITE_SOCIAL_STREAM", null);
+            ContactsPermissions.enforceCallingOrSelfPermission(getContext(),
+                    "android.permission.WRITE_SOCIAL_STREAM");
         }
     }
 
diff --git a/src/com/android/providers/contacts/ProfileProvider.java b/src/com/android/providers/contacts/ProfileProvider.java
index d01195d..ee18a5e 100644
--- a/src/com/android/providers/contacts/ProfileProvider.java
+++ b/src/com/android/providers/contacts/ProfileProvider.java
@@ -15,6 +15,8 @@
  */
 package com.android.providers.contacts;
 
+import com.android.providers.contacts.util.ContactsPermissions;
+
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -51,7 +53,7 @@
      */
     public void enforceReadPermission(Uri uri) {
         if (!mDelegate.isValidPreAuthorizedUri(uri)) {
-            mDelegate.getContext().enforceCallingOrSelfPermission(READ_PERMISSION, null);
+            ContactsPermissions.enforceCallingOrSelfPermission(getContext(), READ_PERMISSION);
         }
     }
 
@@ -59,7 +61,7 @@
      * Performs a permission check on the write profile permission.
      */
     public void enforceWritePermission() {
-        mDelegate.getContext().enforceCallingOrSelfPermission(WRITE_PERMISSION, null);
+        ContactsPermissions.enforceCallingOrSelfPermission(getContext(), WRITE_PERMISSION);
     }
 
     @Override
diff --git a/src/com/android/providers/contacts/VoicemailContentProvider.java b/src/com/android/providers/contacts/VoicemailContentProvider.java
index ba51b8e..e4f6a05 100644
--- a/src/com/android/providers/contacts/VoicemailContentProvider.java
+++ b/src/com/android/providers/contacts/VoicemailContentProvider.java
@@ -35,6 +35,7 @@
 import android.util.Log;
 
 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.util.ContactsPermissions;
 import com.android.providers.contacts.util.SelectionBuilder;
 import com.android.providers.contacts.util.TypedUriMatcherImpl;
 import com.google.common.annotations.VisibleForTesting;
@@ -291,8 +292,8 @@
     private UriData checkPermissionsAndCreateUriDataForRead(Uri uri) {
         // If the caller has been explicitly granted read permission to this URI then no need to
         // check further.
-        if (context().checkCallingUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                == PackageManager.PERMISSION_GRANTED) {
+        if (ContactsPermissions.hasCallerUriPermission(
+                getContext(), uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)) {
             return UriData.createUriData(uri);
         }
 
diff --git a/src/com/android/providers/contacts/VoicemailPermissions.java b/src/com/android/providers/contacts/VoicemailPermissions.java
index e1c9b57..50f2447 100644
--- a/src/com/android/providers/contacts/VoicemailPermissions.java
+++ b/src/com/android/providers/contacts/VoicemailPermissions.java
@@ -16,8 +16,9 @@
 
 package com.android.providers.contacts;
 
+import com.android.providers.contacts.util.ContactsPermissions;
+
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.telecom.DefaultDialerManager;
 
 /**
@@ -105,13 +106,11 @@
 
     /** Determines if the given package has the given permission. */
     private boolean packageHasPermission(String packageName, String permission) {
-        return mContext.getPackageManager().checkPermission(permission, packageName)
-               == PackageManager.PERMISSION_GRANTED;
+        return ContactsPermissions.hasPackagePermission(mContext, permission, packageName);
     }
 
     /** Determines if the calling process has the given permission. */
     private boolean callerHasPermission(String permission) {
-        return mContext.checkCallingOrSelfPermission(permission)
-                == PackageManager.PERMISSION_GRANTED;
+        return ContactsPermissions.hasCallerOrSelfPermission(mContext, permission);
     }
 }
diff --git a/src/com/android/providers/contacts/util/ContactsPermissions.java b/src/com/android/providers/contacts/util/ContactsPermissions.java
new file mode 100644
index 0000000..6dda50b
--- /dev/null
+++ b/src/com/android/providers/contacts/util/ContactsPermissions.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.providers.contacts.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Process;
+import android.util.Log;
+
+public class ContactsPermissions {
+    private static final String TAG = "ContactsPermissions";
+
+    private static final boolean DEBUG = false; // DO NOT submit with true
+
+    // Normally, we allow calls from self, *except* in unit tests, where we clear this flag
+    // to emulate calls from other apps.
+    public static boolean ALLOW_SELF_CALL = true;
+
+    private ContactsPermissions() {
+    }
+
+    public static boolean hasCallerOrSelfPermission(Context context, String permission) {
+        boolean ok = false;
+
+        if (ALLOW_SELF_CALL && Binder.getCallingPid() == Process.myPid()) {
+            ok = true; // Called by self; always allow.
+        } else {
+            ok = context.checkCallingOrSelfPermission(permission)
+                    == PackageManager.PERMISSION_GRANTED;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "hasCallerOrSelfPermission: "
+                    + " perm=" + permission
+                    + " caller=" + Binder.getCallingPid()
+                    + " self=" + Process.myPid()
+                    + " ok=" + ok);
+        }
+        return ok;
+    }
+
+    public static void enforceCallingOrSelfPermission(Context context, String permission) {
+        final boolean ok = hasCallerOrSelfPermission(context, permission);
+        if (!ok) {
+            throw new SecurityException(String.format("The caller must have the %s permission.",
+                    permission));
+        }
+    }
+
+    public static boolean hasPackagePermission(Context context, String permission, String pkg) {
+        boolean ok = false;
+        if (ALLOW_SELF_CALL && context.getPackageName().equals(pkg)) {
+            ok =  true; // Called by self; always allow.
+        } else {
+            ok = context.getPackageManager().checkPermission(permission, pkg)
+                    == PackageManager.PERMISSION_GRANTED;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "hasCallerOrSelfPermission: "
+                    + " perm=" + permission
+                    + " pkg=" + pkg
+                    + " self=" + context.getPackageName()
+                    + " ok=" + ok);
+        }
+        return ok;
+    }
+
+    public static boolean hasCallerUriPermission(Context context, Uri uri, int modeFlags) {
+        boolean ok = false;
+        if (ALLOW_SELF_CALL && Binder.getCallingPid() == Process.myPid()) {
+            ok =  true; // Called by self; always allow.
+        } else {
+            ok = context.checkCallingUriPermission(uri, modeFlags)
+                    == PackageManager.PERMISSION_GRANTED;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "hasCallerUriPermission: "
+                    + " uri=" + uri
+                    + " caller=" + Binder.getCallingPid()
+                    + " self=" + Process.myPid()
+                    + " ok=" + ok);
+        }
+        return ok;
+    }
+}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 91fceae..15a90fa 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -18,8 +18,6 @@
         package="com.android.providers.contacts.tests"
         android:sharedUserId="android.uid.shared">
 
-    <uses-permission android:name="android.permission.READ_CONTACTS" />
-    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
 
@@ -28,6 +26,11 @@
 
         <!-- Mock contacts sync adapter -->
         <service android:name=".MockSyncAdapter" android:exported="true">
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter" />
+            </intent-filter>
+            <meta-data android:name="android.content.SyncAdapter"
+                android:resource="@xml/mock_syncadapter" />
             <meta-data android:name="android.provider.CONTACTS_STRUCTURE"
                 android:resource="@xml/contacts" />
         </service>
diff --git a/tests/res/xml/mock_syncadapter.xml b/tests/res/xml/mock_syncadapter.xml
new file mode 100644
index 0000000..6f77a55
--- /dev/null
+++ b/tests/res/xml/mock_syncadapter.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+    android:contentAuthority="com.android.contacts"
+    android:accountType="com.android.providers.contacts.tests"
+/>
+<!-- The account type doesn't exist, but looks like that's okay for our purpose. -->
+
diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
index 3778380..45206a3 100644
--- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
@@ -153,10 +153,6 @@
         return mActor.context;
     }
 
-    public void addAuthority(String authority) {
-        mActor.addAuthority(authority);
-    }
-
     public ContentProvider addProvider(Class<? extends ContentProvider> providerClass,
             String authority) throws Exception {
         return mActor.addProvider(providerClass, authority);
diff --git a/tests/src/com/android/providers/contacts/ContactsActor.java b/tests/src/com/android/providers/contacts/ContactsActor.java
index bad7d5d..0b91d4c 100644
--- a/tests/src/com/android/providers/contacts/ContactsActor.java
+++ b/tests/src/com/android/providers/contacts/ContactsActor.java
@@ -65,6 +65,7 @@
 import android.test.mock.MockContext;
 import android.util.Log;
 
+import com.android.providers.contacts.util.ContactsPermissions;
 import com.android.providers.contacts.util.MockSharedPreferences;
 
 import com.google.android.collect.Sets;
@@ -263,6 +264,10 @@
      */
     public ContactsActor(final Context overallContext, String packageName,
             Class<? extends ContentProvider> providerClass, String authority) throws Exception {
+
+        // Force permission check even when called by self.
+        ContactsPermissions.ALLOW_SELF_CALL = false;
+
         resolver = new MockContentResolver();
         context = new RestrictionMockContext(overallContext, packageName, resolver,
                 mGrantedPermissions, mGrantedUriPermissions);
@@ -323,10 +328,6 @@
         return mProviderContext;
     }
 
-    public void addAuthority(String authority) {
-        resolver.addProvider(authority, provider);
-    }
-
     public <T extends ContentProvider> T addProvider(Class<T> providerClass,
             String authority) throws Exception {
         return addProvider(providerClass, authority, mProviderContext);
@@ -342,6 +343,12 @@
         info.authority = stripOutUserIdFromAuthority(authority);
         provider.attachInfoForTesting(providerContext, info);
         resolver.addProvider(authority, provider);
+
+        // In case of LegacyTest, "authority" here is actually multiple authorities.
+        // Register all authority here.
+        for (String a : authority.split(";")) {
+            resolver.addProvider(a, provider);
+        }
         return provider;
     }
 
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index 7b2561b..1e1ef04 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -1793,9 +1793,12 @@
 
         // Note here we use a standalone CP2 so it'll have its own db helper.
         // Also use AlteringUserContext here to report the corp user id.
-        return mActor.addProvider(StandaloneContactsProvider2.class,
+        SynchronousContactsProvider2 provider = mActor.addProvider(
+                StandaloneContactsProvider2.class,
                 "" + MockUserManager.CORP_USER.id + "@com.android.contacts",
                 new AlteringUserContext(mActor.getProviderContext(), MockUserManager.CORP_USER.id));
+        provider.wipeData();
+        return provider;
     }
 
     /**
diff --git a/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java b/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java
index 6697ea6..378c9eb 100644
--- a/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java
+++ b/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java
@@ -61,7 +61,7 @@
 
     @Override
     protected String getAuthority() {
-        return Contacts.AUTHORITY;
+        return Contacts.AUTHORITY + ";" + ContactsContract.AUTHORITY;
     }
 
     public void testPeopleInsert() {
@@ -857,8 +857,6 @@
     }
 
     public void testSettings() throws Exception {
-        mActor.addAuthority(ContactsContract.AUTHORITY);
-
         ContentValues values = new ContentValues();
         values.put(Settings._SYNC_ACCOUNT, "foo");
         values.put(Settings._SYNC_ACCOUNT_TYPE, "bar");
diff --git a/tests/src/com/android/providers/contacts/MockSyncAdapter.java b/tests/src/com/android/providers/contacts/MockSyncAdapter.java
index 9255d28..199c216 100644
--- a/tests/src/com/android/providers/contacts/MockSyncAdapter.java
+++ b/tests/src/com/android/providers/contacts/MockSyncAdapter.java
@@ -27,6 +27,9 @@
 
     @Override
     public IBinder onBind(Intent intent) {
+        // Looks like returning null is okay here, probably because the account type doesn't exist.
+        // If the system complains about it, we need to return a real sync adapter class here,
+        // and in the syncMethod -1 to isSyncable.
         return null;
     }
 }