Merge changes from topic 'carrier-app-race' into nyc-dev

* changes:
  Disable preinstalled carrier apps earlier in boot.
  Move CarrierAppUtils into frameworks/base/telephony.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4c03a33..3b07fe1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -229,6 +229,7 @@
 import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
+import com.android.internal.telephony.CarrierAppUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
@@ -2051,6 +2052,10 @@
         PackageManagerService m = new PackageManagerService(context, installer,
                 factoryTest, onlyCore);
         m.enableSystemUserPackages();
+        // Disable any carrier apps. We do this very early in boot to prevent the apps from being
+        // disabled after already being started.
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(context.getOpPackageName(), m,
+                UserHandle.USER_SYSTEM);
         ServiceManager.addService("package", m);
         return m;
     }
@@ -17057,8 +17062,13 @@
         }
         PackageSetting pkgSetting;
         final int uid = Binder.getCallingUid();
-        final int permission = mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
+        final int permission;
+        if (uid == Process.SYSTEM_UID) {
+            permission = PackageManager.PERMISSION_GRANTED;
+        } else {
+            permission = mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
+        }
         enforceCrossUserPermission(uid, userId,
                 false /* requireFullPermission */, true /* checkShell */, "set enabled");
         final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
new file mode 100644
index 0000000..ca7354f
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -0,0 +1,234 @@
+/*
+ * 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.internal.telephony;
+
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utilities for handling carrier applications.
+ * @hide
+ */
+public final class CarrierAppUtils {
+    private static final String TAG = "CarrierAppUtils";
+
+    private static final boolean DEBUG = false; // STOPSHIP if true
+
+    private CarrierAppUtils() {}
+
+    /**
+     * Handle preinstalled carrier apps which should be disabled until a matching SIM is inserted.
+     *
+     * Evaluates the list of applications in config_disabledUntilUsedPreinstalledCarrierApps. We
+     * want to disable each such application which is present on the system image until the user
+     * inserts a SIM which causes that application to gain carrier privilege (indicating a "match"),
+     * without interfering with the user if they opt to enable/disable the app explicitly.
+     *
+     * So, for each such app, we either disable until used IFF the app is not carrier privileged AND
+     * in the default state (e.g. not explicitly DISABLED/DISABLED_BY_USER/ENABLED), or we enable if
+     * the app is carrier privileged and in either the default state or DISABLED_UNTIL_USED.
+     *
+     * When enabling a carrier app we also grant it default permissions.
+     *
+     * This method is idempotent and is safe to be called at any time; it should be called once at
+     * system startup prior to any application running, as well as any time the set of carrier
+     * privileged apps may have changed.
+     */
+    public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+            IPackageManager packageManager, TelephonyManager telephonyManager, int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
+        }
+        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager, userId,
+                systemCarrierAppsDisabledUntilUsed);
+    }
+
+    /**
+     * Like {@link #disableCarrierAppsUntilPrivileged(String, IPackageManager, TelephonyManager,
+     * int)}, but assumes that no carrier apps have carrier privileges.
+     *
+     * This prevents a potential race condition on first boot - since the app's default state is
+     * enabled, we will initially disable it when the telephony stack is first initialized as it has
+     * not yet read the carrier privilege rules. However, since telephony is initialized later on
+     * late in boot, the app being disabled may have already been started in response to certain
+     * broadcasts. The app will continue to run (briefly) after being disabled, before the Package
+     * Manager can kill it, and this can lead to crashes as the app is in an unexpected state.
+     */
+    public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+            IPackageManager packageManager, int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
+        }
+        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        disableCarrierAppsUntilPrivileged(callingPackage, packageManager,
+                null /* telephonyManager */, userId, systemCarrierAppsDisabledUntilUsed);
+    }
+
+    // Must be public b/c framework unit tests can't access package-private methods.
+    @VisibleForTesting
+    public static void disableCarrierAppsUntilPrivileged(String callingPackage,
+            IPackageManager packageManager, @Nullable TelephonyManager telephonyManager, int userId,
+            String[] systemCarrierAppsDisabledUntilUsed) {
+        List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
+                userId, systemCarrierAppsDisabledUntilUsed);
+        if (candidates == null || candidates.isEmpty()) {
+            return;
+        }
+
+        List<String> enabledCarrierPackages = new ArrayList<>();
+
+        try {
+            for (ApplicationInfo ai : candidates) {
+                String packageName = ai.packageName;
+                boolean hasPrivileges = telephonyManager != null &&
+                        telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
+                                TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+
+                // Only update enabled state for the app on /system. Once it has been updated we
+                // shouldn't touch it.
+                if (!ai.isUpdatedSystemApp()) {
+                    if (hasPrivileges
+                            && (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                            || ai.enabledSetting ==
+                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+                        Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
+                                + userId);
+                        packageManager.setApplicationEnabledSetting(packageName,
+                                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                                PackageManager.DONT_KILL_APP, userId, callingPackage);
+                    } else if (!hasPrivileges
+                            && ai.enabledSetting ==
+                            PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+                        Slog.i(TAG, "Update state(" + packageName
+                                + "): DISABLED_UNTIL_USED for user " + userId);
+                        packageManager.setApplicationEnabledSetting(packageName,
+                                PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
+                                userId, callingPackage);
+                    }
+                }
+
+                // Always re-grant default permissions to carrier apps w/ privileges.
+                if (hasPrivileges) {
+                    enabledCarrierPackages.add(ai.packageName);
+                }
+            }
+
+            if (!enabledCarrierPackages.isEmpty()) {
+                // Since we enabled at least one app, ensure we grant default permissions to those
+                // apps.
+                String[] packageNames = new String[enabledCarrierPackages.size()];
+                enabledCarrierPackages.toArray(packageNames);
+                packageManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId);
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not reach PackageManager", e);
+        }
+    }
+
+    /**
+     * Returns the list of "default" carrier apps.
+     *
+     * This is the subset of apps returned by
+     * {@link #getDefaultCarrierAppCandidates(IPackageManager, int)} which currently have carrier
+     * privileges per the SIM(s) inserted in the device.
+     */
+    public static List<ApplicationInfo> getDefaultCarrierApps(IPackageManager packageManager,
+            TelephonyManager telephonyManager, int userId) {
+        // Get all system apps from the default list.
+        List<ApplicationInfo> candidates = getDefaultCarrierAppCandidates(packageManager, userId);
+        if (candidates == null || candidates.isEmpty()) {
+            return null;
+        }
+
+        // Filter out apps without carrier privileges.
+        // Iterate from the end to avoid creating an Iterator object and because we will be removing
+        // elements from the list as we pass through it.
+        for (int i = candidates.size() - 1; i >= 0; i--) {
+            ApplicationInfo ai = candidates.get(i);
+            String packageName = ai.packageName;
+            boolean hasPrivileges =
+                    telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
+                            TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+            if (!hasPrivileges) {
+                candidates.remove(i);
+            }
+        }
+
+        return candidates;
+    }
+
+    /**
+     * Returns the list of "default" carrier app candidates.
+     *
+     * These are the apps subject to the hiding/showing logic in
+     * {@link CarrierAppUtils#disableCarrierAppsUntilPrivileged(String, IPackageManager,
+     * TelephonyManager, int)}, as well as the apps which should have default permissions granted,
+     * when a matching SIM is inserted.
+     *
+     * Whether or not the app is actually considered a default app depends on whether the app has
+     * carrier privileges as determined by the SIMs in the device.
+     */
+    public static List<ApplicationInfo> getDefaultCarrierAppCandidates(
+            IPackageManager packageManager, int userId) {
+        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        return getDefaultCarrierAppCandidatesHelper(packageManager, userId,
+                systemCarrierAppsDisabledUntilUsed);
+    }
+
+    private static List<ApplicationInfo> getDefaultCarrierAppCandidatesHelper(
+            IPackageManager packageManager, int userId,
+            String[] systemCarrierAppsDisabledUntilUsed) {
+        if (systemCarrierAppsDisabledUntilUsed == null
+                || systemCarrierAppsDisabledUntilUsed.length == 0) {
+            return null;
+        }
+        List<ApplicationInfo> apps = null;
+        try {
+            apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length);
+            for (String packageName : systemCarrierAppsDisabledUntilUsed) {
+                ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
+                        PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId);
+                if (ai == null) {
+                    // No app found for packageName
+                    continue;
+                }
+                if (!ai.isSystemApp()) {
+                    continue;
+                }
+                apps.add(ai);
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not reach PackageManager", e);
+        }
+        return apps;
+    }
+}