Allowing enable/disable of phone accounts. (2/3)

- Change broadcast listener to listen to PACKAGE_FULLY_REMOVED instead
of PACKAGE_REMOVED.  This ensures re-installing an app will not
remove all PhoneAccounts, losing the enabled states.
- Changed some method names.
- Added PhoneAccountRegistrar methods to enable/disable PhoneAccounts.
- Changed PhoneAccountRegistrar register method to copy over the
previous enabled state for phone accounts.
- Added some TelecomService methods.


Bug: 17306514
Bug: 17408536

Change-Id: Ie8c5e9f3ddec988b42bee682c91671cec904076b
diff --git a/src/com/android/telecomm/PhoneAccountRegistrar.java b/src/com/android/telecomm/PhoneAccountRegistrar.java
index 4223b63..7aa9261 100644
--- a/src/com/android/telecomm/PhoneAccountRegistrar.java
+++ b/src/com/android/telecomm/PhoneAccountRegistrar.java
@@ -78,7 +78,7 @@
 
     private static final String FILE_NAME = "phone-account-registrar-state.xml";
     @VisibleForTesting
-    public static final int EXPECTED_STATE_VERSION = 2;
+    public static final int EXPECTED_STATE_VERSION = 3;
 
     /** Keep in sync with the same in SipSettings.java */
     private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES";
@@ -118,7 +118,7 @@
             }
         }
 
-        List<PhoneAccountHandle> outgoing = getOutgoingPhoneAccounts(uriScheme);
+        List<PhoneAccountHandle> outgoing = getEnabledPhoneAccounts(uriScheme);
         switch (outgoing.size()) {
             case 0:
                 // There are no accounts, so there can be no default
@@ -166,7 +166,8 @@
                 return;
             }
 
-            if (!has(getPhoneAccount(accountHandle), PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
+            if (!getPhoneAccount(accountHandle).hasCapabilities(
+                    PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
                 Log.w(this, "Trying to set non-call-provider default outgoing %s",
                         accountHandle);
                 return;
@@ -189,7 +190,8 @@
             if (callManagerAccount == null) {
                 Log.d(this, "setSimCallManager: Nonexistent call manager: %s", callManager);
                 return;
-            } else if (!has(callManagerAccount, PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) {
+            } else if (!callManagerAccount.hasCapabilities(
+                    PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) {
                 Log.d(this, "setSimCallManager: Not a call manager: %s", callManagerAccount);
                 return;
             }
@@ -251,6 +253,11 @@
         return null;
     }
 
+    /**
+     * Retrieves a list of all {@link PhoneAccountHandle}s registered.
+     *
+     * @return The list of {@link PhoneAccountHandle}s.
+     */
     public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
         List<PhoneAccountHandle> accountHandles = new ArrayList<>();
         for (PhoneAccount m : mState.accounts) {
@@ -263,17 +270,45 @@
         return new ArrayList<>(mState.accounts);
     }
 
-    public List<PhoneAccountHandle> getOutgoingPhoneAccounts() {
+    /**
+     * Determines the number of enabled and disabled {@link PhoneAccount}s.
+     *
+     * @return The number of enabled and disabled {@link PhoneAccount}s
+     */
+    public int getAllPhoneAccountsCount() {
+        return mState.accounts.size();
+    }
+
+    /**
+     * Retrieves a list of all enabled call provider phone accounts.
+     *
+     * @return The phone account handles.
+     */
+    public List<PhoneAccountHandle> getEnabledPhoneAccounts() {
         return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CALL_PROVIDER);
     }
 
-    public List<PhoneAccountHandle> getOutgoingPhoneAccounts(String uriScheme) {
-        return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CALL_PROVIDER, uriScheme);
+    /**
+     * Retrieves a list of all enabled phone account call provider phone accounts supporting the
+     * specified URI scheme.
+     *
+     * @param uriScheme The URI scheme.
+     * @return The phone account handles.
+     */
+    public List<PhoneAccountHandle> getEnabledPhoneAccounts(String uriScheme) {
+        return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CALL_PROVIDER, uriScheme,
+                false /* includeDisabled */);
     }
 
-    public List<PhoneAccountHandle> getAllConnectionManagerPhoneAccounts() {
+    /**
+     * Retrieves a list of all enabled phone account handles with the connection manager capability.
+     *
+     * @return The phone account handles.
+     */
+    public List<PhoneAccountHandle> getConnectionManagerPhoneAccounts() {
         if (isEnabledConnectionManager()) {
-            return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CONNECTION_MANAGER);
+            return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CONNECTION_MANAGER,
+                    null /* supportedUriScheme */, false /* includeDisabled */);
         }
         return Collections.emptyList();
     }
@@ -287,6 +322,43 @@
         return null;
     }
 
+    /**
+     * Changes the enabled state of the {@link PhoneAccount} identified by a
+     * {@link PhoneAccountHandle}.
+     *
+     * @param handle The {@link PhoneAccountHandle}.
+     * @param isEnabled The new enabled state of the {@link PhoneAccount}.
+     */
+    public void setPhoneAccountEnabled(PhoneAccountHandle handle, boolean isEnabled) {
+        PhoneAccount existing = getPhoneAccount(handle);
+        if (existing.isEnabled() == isEnabled) {
+            return;
+        }
+
+        // Do not permit PhoneAccounts which are marked as always enabled to be disabled.
+        if (existing.hasCapabilities(PhoneAccount.CAPABILITY_ALWAYS_ENABLED)) {
+            return;
+        }
+
+        // If we are disabling the current default outgoing phone account or Sim call manager we
+        // need to null out those preferences.
+        if (!isEnabled) {
+            if (mState.defaultOutgoing != null && mState.defaultOutgoing.equals(handle)) {
+                setUserSelectedOutgoingPhoneAccount(null);
+            }
+
+            if (mState.simCallManager != null && mState.simCallManager.equals(handle)) {
+                setSimCallManager(null);
+            }
+        }
+
+
+        PhoneAccount.Builder builder = existing.toBuilder().setEnabled(isEnabled);
+        PhoneAccount replacement = builder.build();
+
+        addOrReplacePhoneAccount(replacement);
+    }
+
     // TODO: Should we implement an artificial limit for # of accounts associated with a single
     // ComponentName?
     public void registerPhoneAccount(PhoneAccount account) {
@@ -299,6 +371,22 @@
                     "PhoneAccount connection service requires BIND_CONNECTION_SERVICE permission.");
         }
 
+        // If there is an existing PhoneAccount already registered with this handle, copy its
+        // enabled state to the new phone account.
+        PhoneAccount existing = getPhoneAccount(account.getAccountHandle());
+        if (existing != null) {
+            account = account.toBuilder().setEnabled(existing.isEnabled()).build();
+        }
+
+        addOrReplacePhoneAccount(account);
+    }
+
+    /**
+     * Adds a {@code PhoneAccount}, replacing an existing one if found.
+     *
+     * @param account The {@code PhoneAccount} to add or replace.
+     */
+    private void addOrReplacePhoneAccount(PhoneAccount account) {
         mState.accounts.add(account);
         // Search for duplicates and remove any that are found.
         for (int i = 0; i < mState.accounts.size() - 1; i++) {
@@ -406,33 +494,32 @@
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
 
-    // TODO: Add a corresponding has(...) method to class PhoneAccount itself and remove this one
-    // Return true iff the given account has all the specified capability flags
-    static boolean has(PhoneAccount account, int capability) {
-        return (account.getCapabilities() & capability) == capability;
-    }
-
     /**
      * Returns a list of phone account handles with the specified flag.
      *
      * @param flags Flags which the {@code PhoneAccount} must have.
      */
     private List<PhoneAccountHandle> getPhoneAccountHandles(int flags) {
-        return getPhoneAccountHandles(flags, null);
+        return getPhoneAccountHandles(flags, null, false /* includeDisabled */);
     }
 
     /**
      * Returns a list of phone account handles with the specified flag, supporting the specified
-     * URI scheme.
+     * URI scheme.  By default, only enabled phone accounts are included, unless the
+     * {@code includeDisabled} parameter is set {@code true}.
      *
      * @param flags Flags which the {@code PhoneAccount} must have.
      * @param uriScheme URI schemes the PhoneAccount must handle.  {@code Null} bypasses the
      *                  URI scheme check.
+     * @param includeDisabled When {@code true}, the list of phone accounts handles includes those
+     *                        which are marked as disabled.
      */
-    private List<PhoneAccountHandle> getPhoneAccountHandles(int flags, String uriScheme) {
+    private List<PhoneAccountHandle> getPhoneAccountHandles(int flags, String uriScheme,
+            boolean includeDisabled) {
         List<PhoneAccountHandle> accountHandles = new ArrayList<>();
         for (PhoneAccount m : mState.accounts) {
-            if (has(m, flags) && (uriScheme == null || m.supportsUriScheme(uriScheme))) {
+            if ((includeDisabled || m.isEnabled()) && m.hasCapabilities(flags) &&
+                    (uriScheme == null || m.supportsUriScheme(uriScheme))) {
                 accountHandles.add(m.getAccountHandle());
             }
         }
@@ -596,7 +683,6 @@
                     if (toSerialize != null ){
                         serializer.text(toSerialize);
                     }
-
                     serializer.endTag(null, VALUE_TAG);
                 }
             } else {
@@ -628,6 +714,7 @@
             int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
                 if (parser.getName().equals(VALUE_TAG)) {
+                    parser.next();
                     value = parser.getText();
                     arrayEntries.add(value);
                 }
@@ -725,6 +812,9 @@
         private static final String LABEL = "label";
         private static final String SHORT_DESCRIPTION = "short_description";
         private static final String SUPPORTED_URI_SCHEMES = "supported_uri_schemes";
+        private static final String ENABLED = "enabled";
+        private static final String TRUE = "true";
+        private static final String FALSE = "false";
 
         @Override
         public void writeToXml(PhoneAccount o, XmlSerializer serializer)
@@ -745,6 +835,7 @@
                 writeTextSafely(LABEL, o.getLabel(), serializer);
                 writeTextSafely(SHORT_DESCRIPTION, o.getShortDescription(), serializer);
                 writeStringList(SUPPORTED_URI_SCHEMES, o.getSupportedUriSchemes(), serializer);
+                writeTextSafely(ENABLED, o.isEnabled() ? TRUE : FALSE, serializer);
 
                 serializer.endTag(null, CLASS_PHONE_ACCOUNT);
             }
@@ -762,6 +853,7 @@
                 String label = null;
                 String shortDescription = null;
                 List<String> supportedUriSchemes = null;
+                boolean enabled = false;
 
                 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
                     if (parser.getName().equals(ACCOUNT_HANDLE)) {
@@ -789,6 +881,9 @@
                         shortDescription = parser.getText();
                     } else if (parser.getName().equals(SUPPORTED_URI_SCHEMES)) {
                         supportedUriSchemes = readStringList(parser);
+                    } else if (parser.getName().equals(ENABLED)) {
+                        parser.next();
+                        enabled = parser.getText().equals(TRUE);
                     }
                 }
 
@@ -813,6 +908,17 @@
                     }
                 }
 
+                // Prior to version 3, PhoneAccounts didn't include the enabled option.  Enable
+                // all TelephonyConnectionService phone accounts by default.
+                if (version < 3) {
+                    ComponentName telephonyComponentName = new ComponentName("com.android.phone",
+                            "com.android.services.telephony.TelephonyConnectionService");
+
+                    if (accountHandle.getComponentName().equals(telephonyComponentName)) {
+                        enabled = true;
+                    }
+                }
+
                 return PhoneAccount.builder(accountHandle, label)
                         .setAddress(address)
                         .setSubscriptionAddress(subscriptionAddress)
@@ -820,6 +926,7 @@
                         .setIconResId(iconResId)
                         .setShortDescription(shortDescription)
                         .setSupportedUriSchemes(supportedUriSchemes)
+                        .setEnabled(enabled)
                         .build();
             }
             return null;