From b7e26fb1ad1802025cc3a01eeba6acfbc8f3f444 Mon Sep 17 00:00:00 2001 From: Jeff Davidson Date: Thu, 7 Apr 2016 12:47:20 -0700 Subject: Move CarrierAppUtils into frameworks/base/telephony. This is a no-op refactoring which will allow us to access CarrierAppUtils from PackageManagerService. Bug: 27821069 Change-Id: Id6ac33020395f7fc03b285ffa8c3d421a02270ec --- .../internal/telephony/CarrierAppUtils.java | 211 +++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 telephony/java/com/android/internal/telephony/CarrierAppUtils.java 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 000000000000..2e8215eb7bdd --- /dev/null +++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java @@ -0,0 +1,211 @@ +/* + * 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.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); + } + + // Must be public b/c framework unit tests can't access package-private methods. + @VisibleForTesting + public static void disableCarrierAppsUntilPrivileged(String callingPackage, + IPackageManager packageManager, TelephonyManager telephonyManager, int userId, + String[] systemCarrierAppsDisabledUntilUsed) { + List candidates = getDefaultCarrierAppCandidatesHelper(packageManager, + userId, systemCarrierAppsDisabledUntilUsed); + if (candidates == null || candidates.isEmpty()) { + return; + } + + List enabledCarrierPackages = new ArrayList<>(); + + try { + for (ApplicationInfo ai : candidates) { + String packageName = ai.packageName; + boolean hasPrivileges = + 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 getDefaultCarrierApps(IPackageManager packageManager, + TelephonyManager telephonyManager, int userId) { + // Get all system apps from the default list. + List 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 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 getDefaultCarrierAppCandidatesHelper( + IPackageManager packageManager, int userId, + String[] systemCarrierAppsDisabledUntilUsed) { + if (systemCarrierAppsDisabledUntilUsed == null + || systemCarrierAppsDisabledUntilUsed.length == 0) { + return null; + } + List 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; + } +} -- cgit v1.2.3-59-g8ed1b From e68b127525c23e8e0cbe1e9dee75534d99e2833d Mon Sep 17 00:00:00 2001 From: Jeff Davidson Date: Thu, 7 Apr 2016 12:14:57 -0700 Subject: Disable preinstalled carrier apps earlier in boot. Preinstalled carrier apps start in state DEFAULT (== ENABLED); the telephony stack marks them as DISABLED_UNTIL_USED during initialization, and eventually ENABLED once a SIM for that carrier is inserted. However, this can cause a race as telephony initialization may happen after the carrier app is started, while it is still in the DEFAULT state. In this case, the app is disabled, and though PackageManager will subsequently kill it, this may lead to a race as the app will briefly remain running while disabled. In this state, crashes are likely to occur in the app. So, make sure we perform the first disable as soon as PackageManager is ready. This ensures the app is not started until it has been explicitly enabled. Bug: 27821069 Change-Id: I771d7dde7880fd98b1df3d011be44164abf402f4 --- .../android/server/pm/PackageManagerService.java | 14 +++++++++-- .../internal/telephony/CarrierAppUtils.java | 27 ++++++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a6fce519001c..18ec5c84a3c8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -232,6 +232,7 @@ import com.android.internal.os.IParcelFileDescriptorFactory; 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; @@ -2016,6 +2017,10 @@ public class PackageManagerService extends IPackageManager.Stub { 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; } @@ -17076,8 +17081,13 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } 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 index 2e8215eb7bdd..ca7354f66a1b 100644 --- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java @@ -16,6 +16,7 @@ package com.android.internal.telephony; +import android.annotation.Nullable; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; @@ -69,10 +70,32 @@ public final class CarrierAppUtils { 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, TelephonyManager telephonyManager, int userId, + IPackageManager packageManager, @Nullable TelephonyManager telephonyManager, int userId, String[] systemCarrierAppsDisabledUntilUsed) { List candidates = getDefaultCarrierAppCandidatesHelper(packageManager, userId, systemCarrierAppsDisabledUntilUsed); @@ -85,7 +108,7 @@ public final class CarrierAppUtils { try { for (ApplicationInfo ai : candidates) { String packageName = ai.packageName; - boolean hasPrivileges = + boolean hasPrivileges = telephonyManager != null && telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; -- cgit v1.2.3-59-g8ed1b