diff options
-rw-r--r-- | api/system-current.txt | 3 | ||||
-rw-r--r-- | core/java/android/app/ApplicationPackageManager.java | 18 | ||||
-rw-r--r-- | core/java/android/content/pm/IPackageManager.aidl | 4 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageManager.java | 33 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageUserState.java | 7 | ||||
-rw-r--r-- | core/java/com/android/internal/app/HarmfulAppWarningActivity.java | 99 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 11 | ||||
-rw-r--r-- | core/res/res/values/strings.xml | 10 | ||||
-rw-r--r-- | core/res/res/values/symbols.xml | 4 | ||||
-rw-r--r-- | packages/Shell/AndroidManifest.xml | 1 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ActivityStartInterceptor.java | 63 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerService.java | 46 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerShellCommand.java | 36 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/PackageSettingBase.java | 14 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/Settings.java | 41 | ||||
-rw-r--r-- | test-mock/src/android/test/mock/MockPackageManager.java | 16 |
16 files changed, 383 insertions, 23 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index b2d5a49c822c..ee6622ec1040 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -153,6 +153,7 @@ package android { field public static final java.lang.String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH"; field public static final java.lang.String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE"; field public static final java.lang.String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP"; + field public static final java.lang.String SET_HARMFUL_APP_WARNINGS = "android.permission.SET_HARMFUL_APP_WARNINGS"; field public static final java.lang.String SET_MEDIA_KEY_LISTENER = "android.permission.SET_MEDIA_KEY_LISTENER"; field public static final java.lang.String SET_ORIENTATION = "android.permission.SET_ORIENTATION"; field public static final java.lang.String SET_POINTER_SPEED = "android.permission.SET_POINTER_SPEED"; @@ -896,6 +897,7 @@ package android.content.pm { method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String); method public android.content.pm.dex.ArtManager getArtManager(); method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int); + method public java.lang.CharSequence getHarmfulAppWarning(java.lang.String); method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int); method public abstract android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String); method public abstract android.content.ComponentName getInstantAppInstallerComponent(); @@ -912,6 +914,7 @@ package android.content.pm { method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener); method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle); method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int); + method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence); method public abstract void setUpdateAvailable(java.lang.String, boolean); method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int); method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 82f2bacbcd39..4048e6596cfc 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2738,6 +2738,24 @@ public class ApplicationPackageManager extends PackageManager { } @Override + public CharSequence getHarmfulAppWarning(String packageName) { + try { + return mPM.getHarmfulAppWarning(packageName, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public void setHarmfulAppWarning(String packageName, CharSequence warning) { + try { + mPM.setHarmfulAppWarning(packageName, warning, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override public ArtManager getArtManager() { synchronized (mLock) { if (mArtManager == null) { diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index e319750dfaa6..cce6b848e6ae 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -652,4 +652,8 @@ interface IPackageManager { String getInstantAppAndroidId(String packageName, int userId); IArtManager getArtManager(); + + void setHarmfulAppWarning(String packageName, CharSequence warning, int userId); + + CharSequence getHarmfulAppWarning(String packageName, int userId); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 6cd4285f51e3..8a6484c41bce 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5862,4 +5862,37 @@ public abstract class PackageManager { public @NonNull ArtManager getArtManager() { throw new UnsupportedOperationException("getArtManager not implemented in subclass"); } + + /** + * Sets or clears the harmful app warning details for the given app. + * + * When set, any attempt to launch an activity in this package will be intercepted and a + * warning dialog will be shown to the user instead, with the given warning. The user + * will have the option to proceed with the activity launch, or to uninstall the application. + * + * @param packageName The full name of the package to warn on. + * @param warning A warning string to display to the user describing the threat posed by the + * application, or null to clear the warning. + * + * @hide + */ + @RequiresPermission(Manifest.permission.SET_HARMFUL_APP_WARNINGS) + @SystemApi + public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning) { + throw new UnsupportedOperationException("setHarmfulAppWarning not implemented in subclass"); + } + + /** + * Returns the harmful app warning string for the given app, or null if there is none set. + * + * @param packageName The full name of the desired package. + * + * @hide + */ + @RequiresPermission(Manifest.permission.SET_HARMFUL_APP_WARNINGS) + @Nullable + @SystemApi + public CharSequence getHarmfulAppWarning(@NonNull String packageName) { + throw new UnsupportedOperationException("getHarmfulAppWarning not implemented in subclass"); + } } diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 069b2d4e02b5..293beb2bf7bb 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -52,6 +52,7 @@ public class PackageUserState { public int appLinkGeneration; public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED; public int installReason; + public String harmfulAppWarning; public ArraySet<String> disabledComponents; public ArraySet<String> enabledComponents; @@ -87,6 +88,7 @@ public class PackageUserState { enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents); overlayPaths = o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length); + harmfulAppWarning = o.harmfulAppWarning; } /** @@ -247,6 +249,11 @@ public class PackageUserState { } } } + if (harmfulAppWarning == null && oldState.harmfulAppWarning != null + || (harmfulAppWarning != null + && !harmfulAppWarning.equals(oldState.harmfulAppWarning))) { + return false; + } return true; } } diff --git a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java new file mode 100644 index 000000000000..042da36c98f1 --- /dev/null +++ b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 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.app; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentSender; +import android.os.Bundle; +import android.util.Log; +import com.android.internal.R; + +/** + * This dialog is shown to the user before an activity in a harmful app is launched. + * + * See {@code PackageManager.setHarmfulAppInfo} for more info. + */ +public class HarmfulAppWarningActivity extends AlertActivity implements + DialogInterface.OnClickListener { + private static final String TAG = "HarmfulAppWarningActivity"; + + private static final String EXTRA_HARMFUL_APP_WARNING = "harmful_app_warning"; + + private String mPackageName; + private String mHarmfulAppWarning; + private IntentSender mTarget; + + // [b/63909431] STOPSHIP replace placeholder UI with final Harmful App Warning UI + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); + mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT); + mHarmfulAppWarning = intent.getStringExtra(EXTRA_HARMFUL_APP_WARNING); + + if (mPackageName == null || mTarget == null || mHarmfulAppWarning == null) { + Log.wtf(TAG, "Invalid intent: " + intent.toString()); + finish(); + } + + AlertController.AlertParams p = mAlertParams; + p.mTitle = getString(R.string.harmful_app_warning_title); + p.mMessage = mHarmfulAppWarning; + p.mPositiveButtonText = getString(R.string.harmful_app_warning_launch_anyway); + p.mPositiveButtonListener = this; + p.mNegativeButtonText = getString(R.string.harmful_app_warning_uninstall); + p.mNegativeButtonListener = this; + + mAlert.installContent(mAlertParams); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + getPackageManager().setHarmfulAppWarning(mPackageName, null); + + IntentSender target = getIntent().getParcelableExtra(Intent.EXTRA_INTENT); + try { + startIntentSenderForResult(target, -1, null, 0, 0, 0); + } catch (IntentSender.SendIntentException e) { + // ignore.. + } + finish(); + break; + case DialogInterface.BUTTON_NEGATIVE: + getPackageManager().deletePackage(mPackageName, null, 0); + finish(); + break; + } + } + + public static Intent createHarmfulAppWarningIntent(Context context, String targetPackageName, + IntentSender target, CharSequence harmfulAppWarning) { + Intent intent = new Intent(); + intent.setClass(context, HarmfulAppWarningActivity.class); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName); + intent.putExtra(Intent.EXTRA_INTENT, target); + intent.putExtra(EXTRA_HARMFUL_APP_WARNING, harmfulAppWarning); + return intent; + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 00e3a4a68f78..aab7f65bf9f1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3304,6 +3304,10 @@ <permission android:name="android.permission.BIND_PACKAGE_VERIFIER" android:protectionLevel="signature" /> + <!-- @SystemApi @hide Allows an application to mark other applications as harmful --> + <permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" + android:protectionLevel="signature|verifier" /> + <!-- @SystemApi @hide Intent filter verifier needs to have this permission before the PackageManager will trust it to verify intent filters. --> @@ -3869,6 +3873,13 @@ android:excludeFromRecents="true"> </activity> + <activity android:name="com.android.internal.app.HarmfulAppWarningActivity" + android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert" + android:excludeFromRecents="true" + android:process=":ui" + android:exported="false"> + </activity> + <receiver android:name="com.android.server.BootReceiver" android:systemUserOnly="true"> <intent-filter android:priority="1000"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 0618a820be4d..9900b16655d3 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4795,4 +4795,14 @@ <!--Battery saver warning. STOPSHIP: Remove it eventually. --> <string name="battery_saver_warning_title" translatable="false">Extreme battery saver</string> + + + <!-- Label for the uninstall button on the harmful app warning dialog. --> + <string name="harmful_app_warning_uninstall">Uninstall</string> + <!-- Label for the launch anyway button on the harmful app warning dialog. --> + <string name="harmful_app_warning_launch_anyway">Launch anyway</string> + <!-- Title for the harmful app warning dialog. --> + <string name="harmful_app_warning_title">Uninstall harmful app?</string> + + </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 22a425174ba0..2092f6222f30 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3205,4 +3205,8 @@ <java-symbol type="array" name="config_screenBrightnessNits" /> <java-symbol type="string" name="shortcut_disabled_reason_unknown" /> + + <java-symbol type="string" name="harmful_app_warning_uninstall" /> + <java-symbol type="string" name="harmful_app_warning_launch_anyway" /> + <java-symbol type="string" name="harmful_app_warning_title" /> </resources> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index b3d635741b86..0f43db052622 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -134,6 +134,7 @@ <!-- Permission needed to access privileged VR APIs --> <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" /> <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE" /> + <uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" /> <application android:label="@string/app_label" android:defaultToDeviceProtectedStorage="true" diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java index 6684f257fb2f..0480646d4b0e 100644 --- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java @@ -30,6 +30,7 @@ import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; import android.app.ActivityOptions; +import android.app.AppGlobals; import android.app.KeyguardManager; import android.app.admin.DevicePolicyManagerInternal; import android.content.Context; @@ -40,10 +41,12 @@ import android.content.pm.ActivityInfo; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.os.Binder; +import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.HarmfulAppWarningActivity; import com.android.internal.app.UnlaunchableAppActivity; import com.android.server.LocalServices; @@ -115,6 +118,15 @@ class ActivityStartInterceptor { mCallingPackage = callingPackage; } + private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) { + final IIntentSender target = mService.getIntentSenderLocked( + INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/, + null /*resultCode*/, 0 /*requestCode*/, + new Intent[] { mIntent }, new String[] { mResolvedType }, + flags, null /*bOptions*/); + return new IntentSender(target); + } + /** * Intercept the launch intent based on various signals. If an interception happened the * internal variables get assigned and need to be read explicitly by the caller. @@ -144,6 +156,11 @@ class ActivityStartInterceptor { // be unlocked when profile's user is running. return true; } + if (interceptHarmfulAppIfNeeded()) { + // If the app has a "harmful app" warning associated with it, we should ask to uninstall + // before issuing the work challenge. + return true; + } return interceptWorkProfileChallengeIfNeeded(); } @@ -152,13 +169,10 @@ class ActivityStartInterceptor { if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) { return false; } - IIntentSender target = mService.getIntentSenderLocked( - INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingUid, mUserId, null, null, 0, - new Intent[] {mIntent}, new String[] {mResolvedType}, - FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT, null); + IntentSender target = createIntentSenderForOriginalIntent(mCallingUid, + FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT); - mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId, - new IntentSender(target)); + mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId, target); mCallingPid = mRealCallingPid; mCallingUid = mRealCallingUid; mResolvedType = null; @@ -240,11 +254,8 @@ class ActivityStartInterceptor { return null; } // TODO(b/28935539): should allow certain activities to bypass work challenge - final IIntentSender target = mService.getIntentSenderLocked( - INTENT_SENDER_ACTIVITY, callingPackage, - Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent }, - new String[]{ resolvedType }, - FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null); + final IntentSender target = createIntentSenderForOriginalIntent(Binder.getCallingUid(), + FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE); final KeyguardManager km = (KeyguardManager) mServiceContext .getSystemService(KEYGUARD_SERVICE); final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId); @@ -254,8 +265,36 @@ class ActivityStartInterceptor { newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | FLAG_ACTIVITY_TASK_ON_HOME); newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName); - newIntent.putExtra(EXTRA_INTENT, new IntentSender(target)); + newIntent.putExtra(EXTRA_INTENT, target); return newIntent; } + private boolean interceptHarmfulAppIfNeeded() { + CharSequence harmfulAppWarning; + try { + harmfulAppWarning = AppGlobals.getPackageManager().getHarmfulAppWarning( + mAInfo.packageName, mUserId); + } catch (RemoteException e) { + return false; + } + + if (harmfulAppWarning == null) { + return false; + } + + final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid, + FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE); + + mIntent = HarmfulAppWarningActivity.createHarmfulAppWarningIntent(mServiceContext, + mAInfo.packageName, target, harmfulAppWarning); + + mCallingPid = mRealCallingPid; + mCallingUid = mRealCallingUid; + mResolvedType = null; + + mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId); + mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); + return true; + } + } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 44aad4405a1b..1dbd9c0d7fcc 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -17,6 +17,7 @@ package com.android.server.pm; import static android.Manifest.permission.DELETE_PACKAGES; +import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS; import static android.Manifest.permission.INSTALL_PACKAGES; import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; @@ -167,7 +168,6 @@ import android.content.pm.PackageList; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManager.LegacyPackageDeleteObserver; -import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.PackageManagerInternal.PackageListObserver; import android.content.pm.PackageParser; import android.content.pm.PackageParser.ActivityIntentInfo; @@ -18267,7 +18267,8 @@ public class PackageManagerService extends IPackageManager.Stub null /*enabledComponents*/, null /*disabledComponents*/, ps.readUserState(nextUserId).domainVerificationStatus, - 0, PackageManager.INSTALL_REASON_UNKNOWN); + 0, PackageManager.INSTALL_REASON_UNKNOWN, + null /*harmfulAppWarning*/); } mSettings.writeKernelMappingLPr(ps); } @@ -23579,6 +23580,47 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } return unusedPackages; } + + @Override + public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning, + int userId) { + final int callingUid = Binder.getCallingUid(); + final int callingAppId = UserHandle.getAppId(callingUid); + + mPermissionManager.enforceCrossUserPermission(callingUid, userId, + true /*requireFullPermission*/, true /*checkShell*/, "setHarmfulAppInfo"); + + if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID && + checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) { + throw new SecurityException("Caller must have the " + + SET_HARMFUL_APP_WARNINGS + " permission."); + } + + synchronized(mPackages) { + mSettings.setHarmfulAppWarningLPw(packageName, warning, userId); + scheduleWritePackageRestrictionsLocked(userId); + } + } + + @Nullable + @Override + public CharSequence getHarmfulAppWarning(@NonNull String packageName, int userId) { + final int callingUid = Binder.getCallingUid(); + final int callingAppId = UserHandle.getAppId(callingUid); + + mPermissionManager.enforceCrossUserPermission(callingUid, userId, + true /*requireFullPermission*/, true /*checkShell*/, "getHarmfulAppInfo"); + + if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID && + checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) { + throw new SecurityException("Caller must have the " + + SET_HARMFUL_APP_WARNINGS + " permission."); + } + + synchronized(mPackages) { + return mSettings.getHarmfulAppWarningLPr(packageName, userId); + } + } } interface PackageSender { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 2d82c469592e..bd1cd3308ea7 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -230,6 +230,8 @@ class PackageManagerShellCommand extends ShellCommand { return runGetInstantAppResolver(); case "has-feature": return runHasFeature(); + case "set-harmful-app-warning": + return runSetHarmfulAppWarning(); default: { String nextArg = getNextArg(); if (nextArg == null) { @@ -1277,7 +1279,7 @@ class PackageManagerShellCommand extends ShellCommand { return runRemoveSplit(packageName, splitName); } - userId = translateUserId(userId, "runUninstall"); + userId = translateUserId(userId, true /*allowAll*/, "runUninstall"); if (userId == UserHandle.USER_ALL) { userId = UserHandle.USER_SYSTEM; flags |= PackageManager.DELETE_ALL_USERS; @@ -2088,6 +2090,29 @@ class PackageManagerShellCommand extends ShellCommand { return 0; } + private int runSetHarmfulAppWarning() throws RemoteException { + int userId = UserHandle.USER_CURRENT; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } else { + getErrPrintWriter().println("Error: Unknown option: " + opt); + return -1; + } + } + + userId = translateUserId(userId, false /*allowAll*/, "runSetHarmfulAppWarning"); + + final String packageName = getNextArgRequired(); + final String warning = getNextArg(); + + mInterface.setHarmfulAppWarning(packageName, warning, userId); + + return 0; + } + private static String checkAbiArgument(String abi) { if (TextUtils.isEmpty(abi)) { throw new IllegalArgumentException("Missing ABI argument"); @@ -2107,14 +2132,14 @@ class PackageManagerShellCommand extends ShellCommand { throw new IllegalArgumentException("ABI " + abi + " not supported on this device"); } - private int translateUserId(int userId, String logContext) { + private int translateUserId(int userId, boolean allowAll, String logContext) { return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), - userId, true, true, logContext, "pm command"); + userId, allowAll, true, logContext, "pm command"); } private int doCreateSession(SessionParams params, String installerPackageName, int userId) throws RemoteException { - userId = translateUserId(userId, "runInstallCreate"); + userId = translateUserId(userId, true /*allowAll*/, "runInstallCreate"); if (userId == UserHandle.USER_ALL) { userId = UserHandle.USER_SYSTEM; params.installFlags |= PackageManager.INSTALL_ALL_USERS; @@ -2634,6 +2659,9 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(""); pw.println(" get-instantapp-resolver"); pw.println(" Return the name of the component that is the current instant app installer."); + pw.println(""); + pw.println(" set-harmful-app-warning [--user <USER_ID>] <PACKAGE> [<WARNING>]"); + pw.println(" Mark the app as harmful with the given warning message."); pw.println(); Intent.printIntentArgsHelp(pw , ""); } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 809e16cbee6c..e3c4c4358e10 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -437,7 +437,8 @@ public abstract class PackageSettingBase extends SettingBase { boolean notLaunched, boolean hidden, boolean suspended, boolean instantApp, boolean virtualPreload, String lastDisableAppCaller, ArraySet<String> enabledComponents, ArraySet<String> disabledComponents, - int domainVerifState, int linkGeneration, int installReason) { + int domainVerifState, int linkGeneration, int installReason, + String harmfulAppWarning) { PackageUserState state = modifyUserState(userId); state.ceDataInode = ceDataInode; state.enabled = enabled; @@ -454,6 +455,7 @@ public abstract class PackageSettingBase extends SettingBase { state.installReason = installReason; state.instantApp = instantApp; state.virtualPreload = virtualPreload; + state.harmfulAppWarning = harmfulAppWarning; } ArraySet<String> getEnabledComponents(int userId) { @@ -620,4 +622,14 @@ public abstract class PackageSettingBase extends SettingBase { proto.end(userToken); } } + + void setHarmfulAppWarning(int userId, String harmfulAppWarning) { + PackageUserState userState = modifyUserState(userId); + userState.harmfulAppWarning = harmfulAppWarning; + } + + String getHarmfulAppWarning(int userId) { + PackageUserState userState = readUserState(userId); + return userState.harmfulAppWarning; + } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 4cf18149d853..c3884d264a99 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -227,6 +227,7 @@ public final class Settings { private static final String ATTR_INSTALL_REASON = "install-reason"; private static final String ATTR_INSTANT_APP = "instant-app"; private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload"; + private static final String ATTR_HARMFUL_APP_WARNING = "harmful-app-warning"; private static final String ATTR_PACKAGE_NAME = "packageName"; private static final String ATTR_FINGERPRINT = "fingerprint"; @@ -742,7 +743,8 @@ public final class Settings { null /*enabledComponents*/, null /*disabledComponents*/, INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, - 0, PackageManager.INSTALL_REASON_UNKNOWN); + 0, PackageManager.INSTALL_REASON_UNKNOWN, + null /*harmfulAppWarning*/); } } } @@ -1680,7 +1682,8 @@ public final class Settings { null /*enabledComponents*/, null /*disabledComponents*/, INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, - 0, PackageManager.INSTALL_REASON_UNKNOWN); + 0, PackageManager.INSTALL_REASON_UNKNOWN, + null /*harmfulAppWarning*/); } return; } @@ -1755,7 +1758,8 @@ public final class Settings { COMPONENT_ENABLED_STATE_DEFAULT); final String enabledCaller = parser.getAttributeValue(null, ATTR_ENABLED_CALLER); - + final String harmfulAppWarning = + parser.getAttributeValue(null, ATTR_HARMFUL_APP_WARNING); final int verifState = XmlUtils.readIntAttribute(parser, ATTR_DOMAIN_VERIFICATON_STATE, PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED); @@ -1792,7 +1796,7 @@ public final class Settings { ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched, hidden, suspended, instantApp, virtualPreload, enabledCaller, enabledComponents, disabledComponents, verifState, linkGeneration, - installReason); + installReason, harmfulAppWarning); } else if (tagName.equals("preferred-activities")) { readPreferredActivitiesLPw(parser, userId); } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) { @@ -2125,6 +2129,10 @@ public final class Settings { serializer.attribute(null, ATTR_INSTALL_REASON, Integer.toString(ustate.installReason)); } + if (ustate.harmfulAppWarning != null) { + serializer.attribute(null, ATTR_HARMFUL_APP_WARNING, + ustate.harmfulAppWarning); + } if (!ArrayUtils.isEmpty(ustate.enabledComponents)) { serializer.startTag(null, TAG_ENABLED_COMPONENTS); for (final String name : ustate.enabledComponents) { @@ -4347,6 +4355,22 @@ public final class Settings { return false; } + void setHarmfulAppWarningLPw(String packageName, CharSequence warning, int userId) { + final PackageSetting pkgSetting = mPackages.get(packageName); + if (pkgSetting == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + pkgSetting.setHarmfulAppWarning(userId, warning == null ? null : warning.toString()); + } + + String getHarmfulAppWarningLPr(String packageName, int userId) { + final PackageSetting pkgSetting = mPackages.get(packageName); + if (pkgSetting == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + return pkgSetting.getHarmfulAppWarning(userId); + } + private static List<UserInfo> getAllUsers(UserManagerService userManager) { long id = Binder.clearCallingIdentity(); try { @@ -4493,11 +4517,14 @@ public final class Settings { pw.print(ps.getNotLaunched(user.id) ? "l" : "L"); pw.print(ps.getInstantApp(user.id) ? "IA" : "ia"); pw.print(ps.getVirtulalPreload(user.id) ? "VPI" : "vpi"); + String harmfulAppWarning = ps.getHarmfulAppWarning(user.id); + pw.print(harmfulAppWarning != null ? "HA" : "ha"); pw.print(","); pw.print(ps.getEnabled(user.id)); String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id); pw.print(","); pw.print(lastDisabledAppCaller != null ? lastDisabledAppCaller : "?"); + pw.print(","); pw.println(); } return; @@ -4772,6 +4799,12 @@ public final class Settings { .getRuntimePermissionStates(user.id), dumpAll); } + String harmfulAppWarning = ps.getHarmfulAppWarning(user.id); + if (harmfulAppWarning != null) { + pw.print(prefix); pw.print(" harmfulAppWarning: "); + pw.println(harmfulAppWarning); + } + if (permissionNames == null) { ArraySet<String> cmp = ps.getDisabledComponents(user.id); if (cmp != null && cmp.size() > 0) { diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java index 7207ebc2a14f..13e3693cada0 100644 --- a/test-mock/src/android/test/mock/MockPackageManager.java +++ b/test-mock/src/android/test/mock/MockPackageManager.java @@ -1174,4 +1174,20 @@ public class MockPackageManager extends PackageManager { public ArtManager getArtManager() { throw new UnsupportedOperationException(); } + + /** + * @hide + */ + @Override + public void setHarmfulAppWarning(String packageName, CharSequence warning) { + throw new UnsupportedOperationException(); + } + + /** + * @hide + */ + @Override + public CharSequence getHarmfulAppWarning(String packageName) { + throw new UnsupportedOperationException(); + } } |