diff options
11 files changed, 213 insertions, 0 deletions
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index c0cd9ec..db4e123 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2046,6 +2046,17 @@ final class ApplicationPackageManager extends PackageManager { } } + /** @hide */ + @Override + public boolean isComponentProtected(String callingPackage, ComponentName componentName) { + try { + return mPM.isComponentProtected(callingPackage, componentName, mContext.getUserId()); + } catch (RemoteException re) { + Log.e(TAG, "Failed to get component protected setting", re); + return false; + } + } + @Override public PackageInstaller getPackageInstaller() { synchronized (mLock) { diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 7c77f54..6d8b5cb 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -519,4 +519,8 @@ interface IPackageManager { void updateIconMapping(String pkgName); ComposedIconInfo getComposedIconInfo(); int processThemeResources(String themePkgName); + + /** Protected Apps */ + boolean isComponentProtected(in String callingPackage, in ComponentName componentName, + int userId); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 8928ad3..529d641 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4561,6 +4561,13 @@ public abstract class PackageManager { public abstract void setComponentProtectedSetting(ComponentName componentName, boolean newState); /** + * Return whether or not a specific component is protected + * @hide + */ + public abstract boolean isComponentProtected(String callingPackage, + ComponentName componentName); + + /** * Adds a {@link CrossProfileIntentFilter}. After calling this method all intents sent from the * user with id sourceUserId can also be be resolved by activities in the user with id * targetUserId if they match the specified intent filter. diff --git a/core/res/res/drawable/stat_notify_protected.xml b/core/res/res/drawable/stat_notify_protected.xml new file mode 100644 index 0000000..d67a348 --- /dev/null +++ b/core/res/res/drawable/stat_notify_protected.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2016 The CyanogenMod 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="26dp" + android:height="24dp" + android:viewportWidth="26" + android:viewportHeight="24"> + + <path + android:pathData="M0 0h24v24H0z" /> + <path + android:fillColor="#000000" + android:pathData="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/values/cm_strings.xml b/core/res/res/values/cm_strings.xml index 38b98c6..f9bfff1 100644 --- a/core/res/res/values/cm_strings.xml +++ b/core/res/res/values/cm_strings.xml @@ -246,4 +246,7 @@ with spaces on either side. [CHAR LIMIT=3] --> <string name="kg_sub_separator" translatable="false">" | "</string> + <!-- Protected Apps Notification --> + <string name="notify_package_component_protected_title">Activity launch blocked</string> + <string name="notify_package_component_protected_text"><xliff:g id="app_name">%1$s</xliff:g> is protected from being launched. Click to authenticate and launch the application.</string> </resources> diff --git a/core/res/res/values/cm_symbols.xml b/core/res/res/values/cm_symbols.xml index b0a1105..3afb3e7 100644 --- a/core/res/res/values/cm_symbols.xml +++ b/core/res/res/values/cm_symbols.xml @@ -149,4 +149,8 @@ <!-- KeyGuard --> <java-symbol type="string" name="kg_sub_separator" /> + <!-- Protected Apps --> + <java-symbol type="drawable" name="stat_notify_protected" /> + <java-symbol type="string" name="notify_package_component_protected_title" /> + <java-symbol type="string" name="notify_package_component_protected_text" /> </resources> diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index a4214cf..8b4030a 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -27,6 +27,7 @@ import static android.system.OsConstants.S_IXOTH; import android.app.PackageInstallObserver; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -3847,4 +3848,21 @@ public class PackageManagerTests extends AndroidTestCase { * how to do tests on updated system apps? * verify updates to system apps cannot be installed on the sdcard. */ + + //CM Tests + public void testIsComponentProtectedFromSamePackage() { + ComponentName testComponentName = new ComponentName("com.android.test", + "com.android.test.component.protected"); + getPm().setComponentProtectedSetting(testComponentName, true); + assertFalse(getPm().isComponentProtected(testComponentName.getPackageName(), + testComponentName)); + } + + public void testIsComponentProtectedFromManagers() { + ComponentName testComponentName = new ComponentName("com.android.test", + "com.android.test.component.protected"); + getPm().setComponentProtectedSetting(testComponentName, true); + assertFalse(getPm().isComponentProtected(testComponentName.getPackageName(), + testComponentName)); + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index bb61149..b40882d 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1399,6 +1399,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 59; static final int POST_PRIVACY_NOTIFICATION_MSG = 60; static final int CANCEL_PRIVACY_NOTIFICATION_MSG = 61; + static final int POST_COMPONENT_PROTECTED_MSG = 62; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -2167,6 +2168,69 @@ public final class ActivityManagerService extends ActivityManagerNative } catch (RemoteException e) { } } break; + case POST_COMPONENT_PROTECTED_MSG: { + INotificationManager inm = NotificationManager.getService(); + if (inm == null) { + return; + } + + Intent targetIntent = (Intent) msg.obj; + if (targetIntent == null) { + return; + } + + int targetUserId = targetIntent.getIntExtra( + "com.android.settings.PROTECTED_APPS_USER_ID", mCurrentUserId); + // Resolve for labels and whatnot + ActivityInfo root = resolveActivityInfo(targetIntent, targetIntent.getFlags(), + targetUserId); + + try { + Intent protectedAppIntent = new Intent(); + protectedAppIntent.setComponent( + new ComponentName("com.android.settings", + "com.android.settings.applications.ProtectedAppsActivity")); + protectedAppIntent.putExtra( + "com.android.settings.PROTECTED_APP_TARGET_INTENT", + targetIntent); + Context context = mContext.createPackageContext("com.android.settings", 0); + String title = mContext.getString( + com.android.internal.R.string + .notify_package_component_protected_title); + String text = mContext.getString( + com.android.internal.R.string + .notify_package_component_protected_text, + root.applicationInfo.loadLabel(mContext.getPackageManager())); + Notification notification = new Notification.Builder(context) + .setSmallIcon(com.android.internal.R.drawable.stat_notify_protected) + .setWhen(0) + .setTicker(title) + .setColor(mContext.getColor( + com.android.internal.R.color + .system_notification_accent_color)) + .setContentTitle(title) + .setContentText(text) + .setDefaults(Notification.DEFAULT_VIBRATE) + .setPriority(Notification.PRIORITY_MAX) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, + protectedAppIntent, PendingIntent.FLAG_CANCEL_CURRENT, null, + new UserHandle(mCurrentUserId))) + .build(); + try { + int[] outId = new int[1]; + inm.enqueueNotificationWithTag("android", "android", null, + R.string.notify_package_component_protected_title, + notification, outId, mCurrentUserId); + } catch (RuntimeException e) { + Slog.w(ActivityManagerService.TAG, + "Error showing notification for protected app component", e); + } catch (RemoteException e) { + } + } catch (NameNotFoundException e) { + Slog.w(TAG, "Unable to create context for protected app notification", e); + } + } break; } } }; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index be90dc8..cb281ec 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -971,6 +971,23 @@ public final class ActivityStackSupervisor implements DisplayListener { // Cannot start a child activity if the parent is not resumed. return ActivityManager.START_CANCELED; } + + try { + //TODO: This needs to be a flushed out API in the future. + if (AppGlobals.getPackageManager() + .isComponentProtected(callingPackage, intent.getComponent(), userId)) { + Message msg = mService.mHandler.obtainMessage( + ActivityManagerService.POST_COMPONENT_PROTECTED_MSG); + //Store start flags, userid + intent.setFlags(startFlags); + intent.putExtra("com.android.settings.PROTECTED_APPS_USER_ID", userId); + msg.obj = intent; + mService.mHandler.sendMessage(msg); + return ActivityManager.START_NOT_CURRENT_USER_ACTIVITY; + } + } catch (RemoteException e) { + e.printStackTrace(); + } final int realCallingPid = Binder.getCallingPid(); final int realCallingUid = Binder.getCallingUid(); int callingPid; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c1fe38e..4987c9e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -336,6 +336,7 @@ public class PackageManagerService extends IPackageManager.Stub { private static final boolean DEBUG_DEXOPT = false; private static final boolean DEBUG_ABI_SELECTION = false; private static final boolean DEBUG_PREBUNDLED_SCAN = false; + private static final boolean DEBUG_PROTECTED = false; static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false; @@ -17195,6 +17196,53 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + public boolean isComponentProtected(String callingPackage, + ComponentName componentName, int userId) { + if (DEBUG_PROTECTED) Log.d(TAG, "Checking if component is protected " + + componentName.flattenToShortString() + " from calling package " + callingPackage); + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "set protected"); + + //Allow managers full access + List<String> protectedComponentManagers = + CMSettings.Secure.getDelimitedStringAsList(mContext.getContentResolver(), + CMSettings.Secure.PROTECTED_COMPONENT_MANAGERS, "|"); + if (protectedComponentManagers.contains(callingPackage)) { + if (DEBUG_PROTECTED) Log.d(TAG, "Calling package is a protected manager, allow"); + return false; + } + + String packageName = componentName.getPackageName(); + String className = componentName.getClassName(); + + //If this component is launched from the same package, allow it. + if (TextUtils.equals(packageName, callingPackage)) { + if (DEBUG_PROTECTED) Log.d(TAG, "Calling package is same as target, allow"); + return false; + } + + PackageSetting pkgSetting; + ArraySet<String> components; + + synchronized (mPackages) { + pkgSetting = mSettings.mPackages.get(packageName); + + if (pkgSetting == null) { + if (className == null) { + throw new IllegalArgumentException( + "Unknown package: " + packageName); + } + throw new IllegalArgumentException( + "Unknown component: " + packageName + + "/" + className); + } + // Get all the protected components + components = pkgSetting.getProtectedComponents(userId); + if (DEBUG_PROTECTED) Log.d(TAG, "Got " + components.size() + " protected components"); + return components.size() > 0; + } + } + + @Override public boolean isStorageLow() { final long token = Binder.clearCallingIdentity(); try { diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 7aacb23..d3e2bfd 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -891,6 +891,7 @@ public class MockPackageManager extends PackageManager { public boolean isUpgrade() { throw new UnsupportedOperationException(); } + /** * @hide */ @@ -903,6 +904,14 @@ public class MockPackageManager extends PackageManager { * @hide */ @Override + public boolean isComponentProtected(String callingPackage, ComponentName componentName) { + throw new UnsupportedOperationException(); + } + + /** + * @hide + */ + @Override public void installPackage(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName) { throw new UnsupportedOperationException(); |