Hide unregistered phone account call log entries.

Change-Id: I238882e0dd3e63747a4eedcf3ff2af2c8d770dd4
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index bb8158a..bc8ddea 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3,18 +3,19 @@
         android:sharedUserId="android.uid.shared"
         android:sharedUserLabel="@string/sharedUserLabel">
 
-    <uses-permission android:name="android.permission.READ_CONTACTS" />
-    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
-    <uses-permission android:name="android.permission.READ_PROFILE" />
-    <uses-permission android:name="android.permission.WRITE_PROFILE" />
-    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
-    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.BIND_DIRECTORY_SEARCH" />
-    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
-    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
     <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.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" />
@@ -71,6 +72,14 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="PhoneAccountRegistrationReceiver"
+                android:permission="android.permission.BROADCAST_PHONE_ACCOUNT_REGISTRATION">
+            <!-- Broadcast sent after a phone account is registered in telecom. -->
+            <intent-filter>
+                <action android:name="android.telecom.action.PHONE_ACCOUNT_REGISTERED"/>
+            </intent-filter>
+        </receiver>
+
         <receiver android:name="PackageIntentReceiver">
             <intent-filter>
                 <action android:name="android.intent.action.PACKAGE_ADDED" />
diff --git a/src/com/android/providers/contacts/CallLogBackupAgent.java b/src/com/android/providers/contacts/CallLogBackupAgent.java
index 8e160c8..6de5a5e 100644
--- a/src/com/android/providers/contacts/CallLogBackupAgent.java
+++ b/src/com/android/providers/contacts/CallLogBackupAgent.java
@@ -289,8 +289,8 @@
                 call.type = dataInput.readInt();
                 call.numberPresentation = dataInput.readInt();
                 call.accountComponentName = readString(dataInput, version);
-                call.accountId = dataInput.readUTF();
-                call.accountAddress = dataInput.readUTF();
+                call.accountId = readString(dataInput, version);
+                call.accountAddress = readString(dataInput, version);
                 call.dataUsage = dataInput.readLong();
                 call.features = dataInput.readInt();
             }
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index 6bf913e..1899393 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -39,6 +39,7 @@
 import android.os.UserManager;
 import android.provider.CallLog;
 import android.provider.CallLog.Calls;
+import android.telecom.PhoneAccountHandle;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -46,7 +47,6 @@
 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
 import com.android.providers.contacts.util.SelectionBuilder;
 import com.android.providers.contacts.util.UserUtils;
-
 import com.google.common.annotations.VisibleForTesting;
 
 import java.util.HashMap;
@@ -60,12 +60,16 @@
     private static final String TAG = CallLogProvider.class.getSimpleName();
 
     private static final int BACKGROUND_TASK_INITIALIZE = 0;
+    private static final int BACKGROUND_TASK_ADJUST_PHONE_ACCOUNT = 1;
 
     /** Selection clause for selecting all calls that were made after a certain time */
     private static final String MORE_RECENT_THAN_SELECTION = Calls.DATE + "> ?";
     /** Selection clause to use to exclude voicemail records.  */
     private static final String EXCLUDE_VOICEMAIL_SELECTION = getInequalityClause(
             Calls.TYPE, Calls.VOICEMAIL_TYPE);
+    /** Selection clause to exclude hidden records. */
+    private static final String EXCLUDE_HIDDEN_SELECTION = getEqualityClause(
+            Calls.PHONE_ACCOUNT_HIDDEN, 0);
 
     @VisibleForTesting
     static final String[] CALL_LOG_SYNC_PROJECTION = new String[] {
@@ -86,6 +90,10 @@
 
     private static final int CALLS_FILTER = 3;
 
+    private static final String ADJUST_FOR_NEW_PHONE_ACCOUNT_QUERY =
+            "UPDATE " + Tables.CALLS + " SET " + Calls.PHONE_ACCOUNT_HIDDEN + "=0 WHERE " +
+            Calls.PHONE_ACCOUNT_COMPONENT_NAME + "=? AND " + Calls.PHONE_ACCOUNT_ID + "=?;";
+
     private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
     static {
         sURIMatcher.addURI(CallLog.AUTHORITY, "calls", CALLS);
@@ -109,6 +117,7 @@
         sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME, Calls.PHONE_ACCOUNT_COMPONENT_NAME);
         sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_ID, Calls.PHONE_ACCOUNT_ID);
         sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_ADDRESS, Calls.PHONE_ACCOUNT_ADDRESS);
+        sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_HIDDEN, Calls.PHONE_ACCOUNT_HIDDEN);
         sCallsProjectionMap.put(Calls.NEW, Calls.NEW);
         sCallsProjectionMap.put(Calls.VOICEMAIL_URI, Calls.VOICEMAIL_URI);
         sCallsProjectionMap.put(Calls.TRANSCRIPTION, Calls.TRANSCRIPTION);
@@ -155,13 +164,13 @@
         mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
             @Override
             public void handleMessage(Message msg) {
-                performBackgroundTask(msg.what);
+                performBackgroundTask(msg.what, msg.obj);
             }
         };
 
         mReadAccessLatch = new CountDownLatch(1);
 
-        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
+        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE, null);
 
         if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
             Log.d(Constants.PERFORMANCE_TAG, "CallLogProvider.onCreate finish");
@@ -190,6 +199,7 @@
 
         final SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
         checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder, true /*isQuery*/);
+        selectionBuilder.addClause(EXCLUDE_HIDDEN_SELECTION);
 
         final int match = sURIMatcher.match(uri);
         switch (match) {
@@ -357,6 +367,10 @@
         return getContext();
     }
 
+    void adjustForNewPhoneAccount(PhoneAccountHandle handle) {
+        scheduleBackgroundTask(BACKGROUND_TASK_ADJUST_PHONE_ACCOUNT, handle);
+    }
+
     /**
      * Returns a {@link DatabaseModifier} that takes care of sending necessary notifications
      * after the operation is performed.
@@ -467,6 +481,16 @@
     }
 
     /**
+     * Un-hides any hidden call log entries that are associated with the specified handle.
+     *
+     * @param handle The handle to the newly registered {@link android.telecom.PhoneAccount}.
+     */
+    private void adjustForNewPhoneAccountInternal(PhoneAccountHandle handle) {
+        mDbHelper.getWritableDatabase().execSQL(ADJUST_FOR_NEW_PHONE_ACCOUNT_QUERY,
+                new String[] { handle.getComponentName().flattenToString(), handle.getId() });
+    }
+
+    /**
      * @param cursor to copy call log entries from
      *
      * @return the timestamp of the last synced entry.
@@ -544,11 +568,11 @@
         }
     }
 
-    private void scheduleBackgroundTask(int task) {
-        mBackgroundHandler.sendEmptyMessage(task);
+    private void scheduleBackgroundTask(int task, Object arg) {
+        mBackgroundHandler.obtainMessage(task, arg).sendToTarget();
     }
 
-    private void performBackgroundTask(int task) {
+    private void performBackgroundTask(int task, Object arg) {
         if (task == BACKGROUND_TASK_INITIALIZE) {
             try {
                 final Context context = getContext();
@@ -563,6 +587,8 @@
                 mReadAccessLatch.countDown();
                 mReadAccessLatch = null;
             }
+        } else if (task == BACKGROUND_TASK_ADJUST_PHONE_ACCOUNT) {
+            adjustForNewPhoneAccountInternal((PhoneAccountHandle) arg);
         }
 
     }
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index fc4d343..1fe874a 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -16,15 +16,14 @@
 
 package com.android.providers.contacts;
 
-import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.CharArrayBuffer;
 import android.database.Cursor;
@@ -90,12 +89,12 @@
 import com.google.android.collect.Sets;
 import com.google.common.annotations.VisibleForTesting;
 
+import libcore.icu.ICU;
+
 import java.util.Locale;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
-import libcore.icu.ICU;
-
 /**
  * Database helper for contacts. Designed as a singleton to make sure that all
  * {@link android.content.ContentProvider} users get the same reference.
@@ -121,7 +120,7 @@
      *   1000-1100 M
      * </pre>
      */
-    static final int DATABASE_VERSION = 1003;
+    static final int DATABASE_VERSION = 1004;
 
     public interface Tables {
         public static final String CONTACTS = "contacts";
@@ -1519,6 +1518,7 @@
                 Calls.PHONE_ACCOUNT_COMPONENT_NAME + " TEXT," +
                 Calls.PHONE_ACCOUNT_ID + " TEXT," +
                 Calls.PHONE_ACCOUNT_ADDRESS + " TEXT," +
+                Calls.PHONE_ACCOUNT_HIDDEN + " INTEGER NOT NULL DEFAULT 0," +
                 Calls.SUB_ID + " INTEGER DEFAULT -1," +
                 Calls.NEW + " INTEGER," +
                 Calls.CACHED_NAME + " TEXT," +
@@ -2881,6 +2881,11 @@
             oldVersion = 1003;
         }
 
+        if (oldVersion < 1004) {
+            upgradeToVersion1004(db);
+            oldVersion = 1004;
+        }
+
         if (upgradeViewsAndTriggers) {
             createContactsViews(db);
             createGroupsView(db);
@@ -4374,6 +4379,14 @@
         }
     }
 
+    /**
+     * Add a "hidden" column for call log entries we want to hide after an upgrade until the user
+     * adds the right phone account to the device.
+     */
+    public void upgradeToVersion1004(SQLiteDatabase db) {
+        db.execSQL("ALTER TABLE calls ADD phone_account_hidden INTEGER NOT NULL DEFAULT 0;");
+    }
+
     public String extractHandleFromEmailAddress(String email) {
         Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
         if (tokens.length == 0) {
diff --git a/src/com/android/providers/contacts/PhoneAccountRegistrationReceiver.java b/src/com/android/providers/contacts/PhoneAccountRegistrationReceiver.java
new file mode 100644
index 0000000..8a68889
--- /dev/null
+++ b/src/com/android/providers/contacts/PhoneAccountRegistrationReceiver.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.provider.CallLog;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+
+/**
+ * This will be launched when a new phone account is registered in telecom. It is used by the call
+ * log to un-hide any entries which were previously hidden after a backup-restore until it's
+ * associated phone-account is registered with telecom.
+ *
+ * IOW, after a restore, we hide call log entries until the user inserts the corresponding SIM,
+ * registers the corresponding SIP account, or registers a corresponding alternative phone-account.
+ */
+public class PhoneAccountRegistrationReceiver extends BroadcastReceiver {
+    static final String TAG = "PhoneAccountReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        // We are now running with the system up, but no apps started,
+        // so can do whatever cleanup after an upgrade that we want.
+        if (TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED.equals(intent.getAction())) {
+
+            PhoneAccountHandle handle = (PhoneAccountHandle) intent.getParcelableExtra(
+                    TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+
+            IContentProvider iprovider =
+                    context.getContentResolver().acquireProvider(CallLog.AUTHORITY);
+            ContentProvider provider = ContentProvider.coerceToLocalContentProvider(iprovider);
+            if (provider instanceof CallLogProvider) {
+                ((CallLogProvider) provider).adjustForNewPhoneAccount(handle);
+            }
+        }
+    }
+}