diff options
15 files changed, 465 insertions, 124 deletions
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index b8b7c553a1db..8f908fb85444 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -2050,6 +2050,8 @@ public class ListView extends AbsListView { p.recycledHeaderFooter = true; } addViewInLayout(child, flowDown ? -1 : 0, p, true); + // add view in layout will reset the RTL properties. We have to re-resolve them + child.resolveRtlPropertiesIfNeeded(); } if (needToMeasure) { diff --git a/docs/html/guide/topics/media/camera.jd b/docs/html/guide/topics/media/camera.jd index 8b79b237fcee..c806c886a3cc 100644 --- a/docs/html/guide/topics/media/camera.jd +++ b/docs/html/guide/topics/media/camera.jd @@ -1243,7 +1243,7 @@ support and to what level. After making that decision, you should plan on includ camera application that checks to see if device hardware supports those features and fails gracefully if a feature is not available.</p> -<p>You can check the availabilty of camera features by getting an instance of a camera’s parameters +<p>You can check the availabilty of camera features by getting an instance of a camera's parameters object, and checking the relevant methods. The following code sample shows you how to obtain a {@link android.hardware.Camera.Parameters} object and check if the camera supports the autofocus feature:</p> @@ -1290,7 +1290,7 @@ mCamera.setParameters(params); <p>This technique works for nearly all camera features, and most parameters can be changed at any time after you have obtained an instance of the {@link android.hardware.Camera} object. Changes to -parameters are typically visible to the user immediately in the application’s camera preview. +parameters are typically visible to the user immediately in the application's camera preview. On the software side, parameter changes may take several frames to actually take effect as the camera hardware processes the new instructions and then sends updated image data.</p> @@ -1341,7 +1341,7 @@ mCamera.setParameters(params); </pre> <p>The {@link android.hardware.Camera.Area} object contains two data parameters: A {@link -android.graphics.Rect} object for specifying an area within the camera’s field of view and a weight +android.graphics.Rect} object for specifying an area within the camera's field of view and a weight value, which tells the camera what level of importance this area should be given in light metering or focus calculations.</p> @@ -1405,7 +1405,7 @@ class MyFaceDetectionListener implements Camera.FaceDetectionListener { } </pre> -<p>After creating this class, you then set it into your application’s +<p>After creating this class, you then set it into your application's {@link android.hardware.Camera} object, as shown in the example code below:</p> <pre> @@ -1480,7 +1480,7 @@ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { <p class="note"><strong>Note:</strong> Remember to call this method <em>after</em> calling {@link android.hardware.Camera#startPreview startPreview()}. Do not attempt to start face detection -in the {@link android.app.Activity#onCreate onCreate()} method of your camera app’s main activity, +in the {@link android.app.Activity#onCreate onCreate()} method of your camera app's main activity, as the preview is not available by this point in your application's the execution.</p> diff --git a/docs/html/guide/topics/ui/accessibility/apps.jd b/docs/html/guide/topics/ui/accessibility/apps.jd index 26fb3cca9dc3..ab8c792cd24b 100644 --- a/docs/html/guide/topics/ui/accessibility/apps.jd +++ b/docs/html/guide/topics/ui/accessibility/apps.jd @@ -160,7 +160,7 @@ focus navigation</a>.</p> {@code android:focusable}</a> attribute is set to {@code true}. This setting allows users to focus on the element using the directional controls and then interact with it. The user interface controls provided by the Android framework are focusable by default and visually indicate focus by changing -the control’s appearance.</p> +the control's appearance.</p> <p>Android provides several APIs that let you control whether a user interface control is focusable and even request that a control be given focus:</p> @@ -436,7 +436,7 @@ public boolean onKeyUp (int keyCode, KeyEvent event) { <h3 id="populate-events">Populating accessibility events</h3> <p>Each {@link android.view.accessibility.AccessibilityEvent} has a set of required properties that -describe the current state of the view. These properties include things such as the view’s class +describe the current state of the view. These properties include things such as the view's class name, content description and checked state. The specific properties required for each event type are described in the {@link android.view.accessibility.AccessibilityEvent} reference documentation. The {@link android.view.View} implementation provides default values for these properties. Many of diff --git a/docs/html/guide/topics/ui/accessibility/services.jd b/docs/html/guide/topics/ui/accessibility/services.jd index f91a9791a469..c6db855adba4 100644 --- a/docs/html/guide/topics/ui/accessibility/services.jd +++ b/docs/html/guide/topics/ui/accessibility/services.jd @@ -124,7 +124,7 @@ your accessibility service, as shown in the following example:</p> </service> </pre> -<p>This meta-data element refers to an XML file that you create in your application’s resource +<p>This meta-data element refers to an XML file that you create in your application's resource directory ({@code <project_dir>/res/xml/accessibility_service_config.xml}). The following code shows example contents for the service configuration file:</p> @@ -267,7 +267,7 @@ this way, the Android system can provide much richer detail about accessibility accessibility services to provide more useful feedback to users.</p> <p>An accessibility service gets information about an user interface event through an {@link -android.view.accessibility.AccessibilityEvent} passed by the system to the service’s +android.view.accessibility.AccessibilityEvent} passed by the system to the service's {@link android.accessibilityservice.AccessibilityService#onAccessibilityEvent onAccessibilityEvent()} callback method. This object provides details about the event, including the type of object being acted upon, its descriptive text and other details. Starting in Android 4.0 @@ -335,7 +335,7 @@ user actions). If your service requires all views, it can request them by settin of a user. This feature, added in Android 4.1 (API Level 16), and requires that your accessibility service request activation of the Explore by Touch feature. Your service can request this activation by setting the - {@link android.accessibilityservice.AccessibilityServiceInfo#flags flags} member of the service’s + {@link android.accessibilityservice.AccessibilityServiceInfo#flags flags} member of the service's {@link android.accessibilityservice.AccessibilityServiceInfo} instance to {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}, as shown in the following example. diff --git a/docs/html/guide/topics/ui/index.jd b/docs/html/guide/topics/ui/index.jd index ccdf200ecac6..7dbc01579ad3 100644 --- a/docs/html/guide/topics/ui/index.jd +++ b/docs/html/guide/topics/ui/index.jd @@ -13,7 +13,7 @@ page.landing.next=overview.html <a href="http://android-developers.blogspot.com/2012/01/say-goodbye-to-menu-button.html"> <h4>Say Goodbye to the Menu Button</h4> - <p>As Ice Cream Sandwich rolls out to more devices, it’s important that you begin to migrate + <p>As Ice Cream Sandwich rolls out to more devices, it's important that you begin to migrate your designs to the action bar in order to promote a consistent Android user experience.</p> </a> <a href="http://android-developers.blogspot.com/2011/11/new-layout-widgets-space-and-gridlayout.html"> @@ -29,7 +29,7 @@ way to interact with your application.</p> </a> <a href="http://android-developers.blogspot.com/2011/08/horizontal-view-swiping-with-viewpager.html"> <h4>Horizontal View Swiping with ViewPager</h4> <p>Whether you have just started out in Android app development or are a veteran of the craft, -it probably won’t be too long before you’ll need to implement horizontally scrolling sets of views. +it probably won't be too long before you'll need to implement horizontally scrolling sets of views. </p> </a> </div> diff --git a/docs/html/guide/topics/ui/settings.jd b/docs/html/guide/topics/ui/settings.jd index 9e304a3f603c..619fd268aef9 100644 --- a/docs/html/guide/topics/ui/settings.jd +++ b/docs/html/guide/topics/ui/settings.jd @@ -943,8 +943,8 @@ uses.</p> <p>The Android framework includes a variety of {@link android.preference.Preference} subclasses that allow you to build a UI for several different types of settings. -However, you might discover a setting you need for which there’s no built-in solution, such as a -number picker or date picker. In such a case, you’ll need to create a custom preference by extending +However, you might discover a setting you need for which there's no built-in solution, such as a +number picker or date picker. In such a case, you'll need to create a custom preference by extending the {@link android.preference.Preference} class or one of the other subclasses.</p> <p>When you extend the {@link android.preference.Preference} class, there are a few important diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b907be07fab9..54c36e111391 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -12302,6 +12302,9 @@ public class PackageManagerService extends IPackageManager.Stub { public void run() { for (int i = 0; i < mRunningInstalls.size(); i++) { final PostInstallData data = mRunningInstalls.valueAt(i); + if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) { + continue; + } if (pkgName.equals(data.res.pkg.applicationInfo.packageName)) { // right package; but is it for the right user? for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 5787bdb48498..e5ddfd09d82b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -1236,7 +1236,7 @@ class PackageManagerShellCommand extends ShellCommand { PackageInstaller.STATUS_FAILURE); if (status == PackageInstaller.STATUS_SUCCESS) { if (logSuccess) { - System.out.println("Success"); + pw.println("Success"); } } else { pw.println("Failure [" diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 185a6a2aadd8..93693ce665c1 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -131,12 +131,8 @@ import java.util.function.Predicate; * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi. * -> But TypedValue.applyDimension() doesn't differentiate x and y..? * - * - Default launcher check does take a few ms. Worth caching. - * * - Detect when already registered instances are passed to APIs again, which might break * internal bitmap handling. - * - * - Add more call stats. */ public class ShortcutService extends IShortcutService.Stub { static final String TAG = "ShortcutService"; @@ -1354,16 +1350,21 @@ public class ShortcutService extends IShortcutService.Stub { } private void enforceSystemOrShell() { - Preconditions.checkState(isCallerSystem() || isCallerShell(), - "Caller must be system or shell"); + if (!(isCallerSystem() || isCallerShell())) { + throw new SecurityException("Caller must be system or shell"); + } } private void enforceShell() { - Preconditions.checkState(isCallerShell(), "Caller must be shell"); + if (!isCallerShell()) { + throw new SecurityException("Caller must be shell"); + } } private void enforceSystem() { - Preconditions.checkState(isCallerSystem(), "Caller must be system"); + if (!isCallerSystem()) { + throw new SecurityException("Caller must be system"); + } } private void enforceResetThrottlingPermission() { @@ -3177,9 +3178,13 @@ public class ShortcutService extends IShortcutService.Stub { enforceShell(); - final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver); - - resultReceiver.send(status, null); + final long token = injectClearCallingIdentity(); + try { + final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver); + resultReceiver.send(status, null); + } finally { + injectRestoreCallingIdentity(token); + } } static class CommandException extends Exception { @@ -3224,9 +3229,6 @@ public class ShortcutService extends IShortcutService.Stub { final PrintWriter pw = getOutPrintWriter(); try { switch (cmd) { - case "reset-package-throttling": - handleResetPackageThrottling(); - break; case "reset-throttling": handleResetThrottling(); break; @@ -3245,9 +3247,6 @@ public class ShortcutService extends IShortcutService.Stub { case "get-default-launcher": handleGetDefaultLauncher(); break; - case "refresh-default-launcher": - handleRefreshDefaultLauncher(); - break; case "unload-user": handleUnloadUser(); break; @@ -3273,9 +3272,6 @@ public class ShortcutService extends IShortcutService.Stub { final PrintWriter pw = getOutPrintWriter(); pw.println("Usage: cmd shortcut COMMAND [options ...]"); pw.println(); - pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE"); - pw.println(" Reset throttling for a package"); - pw.println(); pw.println("cmd shortcut reset-throttling [--user USER_ID]"); pw.println(" Reset throttling for all packages and users"); pw.println(); @@ -3292,10 +3288,7 @@ public class ShortcutService extends IShortcutService.Stub { pw.println(" Clear the cached default launcher"); pw.println(); pw.println("cmd shortcut get-default-launcher [--user USER_ID]"); - pw.println(" Show the cached default launcher"); - pw.println(); - pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]"); - pw.println(" Refresh the cached default launcher"); + pw.println(" Show the default launcher"); pw.println(); pw.println("cmd shortcut unload-user [--user USER_ID]"); pw.println(" Unload a user from the memory"); @@ -3309,7 +3302,7 @@ public class ShortcutService extends IShortcutService.Stub { private void handleResetThrottling() throws CommandException { parseOptions(/* takeUser =*/ true); - Slog.i(TAG, "cmd: handleResetThrottling"); + Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId); resetThrottlingInner(mUserId); } @@ -3320,16 +3313,6 @@ public class ShortcutService extends IShortcutService.Stub { resetAllThrottlingInner(); } - private void handleResetPackageThrottling() throws CommandException { - parseOptions(/* takeUser =*/ true); - - final String packageName = getNextArgRequired(); - - Slog.i(TAG, "cmd: handleResetPackageThrottling: " + packageName); - - resetPackageThrottling(packageName, mUserId); - } - private void handleOverrideConfig() throws CommandException { final String config = getNextArgRequired(); @@ -3375,12 +3358,6 @@ public class ShortcutService extends IShortcutService.Stub { private void handleGetDefaultLauncher() throws CommandException { parseOptions(/* takeUser =*/ true); - showLauncher(); - } - - private void handleRefreshDefaultLauncher() throws CommandException { - parseOptions(/* takeUser =*/ true); - clearLauncher(); showLauncher(); } @@ -3388,7 +3365,7 @@ public class ShortcutService extends IShortcutService.Stub { private void handleUnloadUser() throws CommandException { parseOptions(/* takeUser =*/ true); - Slog.i(TAG, "cmd: handleUnloadUser: " + mUserId); + Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId); ShortcutService.this.handleCleanupUser(mUserId); } @@ -3397,7 +3374,7 @@ public class ShortcutService extends IShortcutService.Stub { parseOptions(/* takeUser =*/ true); final String packageName = getNextArgRequired(); - Slog.i(TAG, "cmd: handleClearShortcuts: " + mUserId + ", " + packageName); + Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName); ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId, /* appStillExists = */ true); diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index ff1c3b6b8ae0..ecebbc4dd9d0 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -614,6 +614,14 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected final Map<Integer, Boolean> mRunningUsers = new HashMap<>(); protected final Map<Integer, Boolean> mUnlockedUsers = new HashMap<>(); + protected static final String PACKAGE_SYSTEM_LAUNCHER = "com.android.systemlauncher"; + protected static final String PACKAGE_SYSTEM_LAUNCHER_NAME = "systemlauncher_name"; + protected static final int PACKAGE_SYSTEM_LAUNCHER_PRIORITY = 0; + + protected static final String PACKAGE_FALLBACK_LAUNCHER = "com.android.settings"; + protected static final String PACKAGE_FALLBACK_LAUNCHER_NAME = "fallback"; + protected static final int PACKAGE_FALLBACK_LAUNCHER_PRIORITY = -999; + static { QUERY_ALL.setQueryFlags( ShortcutQuery.FLAG_GET_ALL_KINDS); @@ -1866,4 +1874,40 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } return sb.toString(); } + + protected void prepareGetHomeActivitiesAsUser(ComponentName preferred, + List<ResolveInfo> candidates, int userId) { + doAnswer(inv -> { + ((List) inv.getArguments()[0]).addAll(candidates); + return preferred; + }).when(mMockPackageManagerInternal).getHomeActivitiesAsUser(any(List.class), eq(userId)); + } + + protected static ComponentName cn(String packageName, String name) { + return new ComponentName(packageName, name); + } + + protected static ResolveInfo ri(String packageName, String name, boolean isSystem, int priority) { + final ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = new ActivityInfo(); + ri.activityInfo.applicationInfo = new ApplicationInfo(); + + ri.activityInfo.packageName = packageName; + ri.activityInfo.name = name; + if (isSystem) { + ri.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; + } + ri.priority = priority; + return ri; + } + + protected static ResolveInfo getSystemLauncher() { + return ri(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME, true, + PACKAGE_SYSTEM_LAUNCHER_PRIORITY); + } + + protected static ResolveInfo getFallbackLauncher() { + return ri(PACKAGE_FALLBACK_LAUNCHER, PACKAGE_FALLBACK_LAUNCHER_NAME, true, + PACKAGE_FALLBACK_LAUNCHER_PRIORITY); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index 5206507f898a..82ccd36c260f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -107,15 +107,6 @@ import java.util.Locale; -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest1 \ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner - - - * TODO More tests for pinning + manifest shortcuts - * TODO Manifest shortcuts + app upgrade -> launcher callback. - * Also locale change should trigger launcehr callbacks too, when they use strign resoucres. - * (not implemented yet.) - * TODO: Add checks with assertAllNotHaveIcon() - * TODO: Detailed test for hasShortcutPermissionInner(). - * TODO: Add tests for the command line functions too. */ @SmallTest public class ShortcutManagerTest1 extends BaseShortcutManagerTest { @@ -4068,7 +4059,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); // Clear data - mService.mPackageMonitor.onReceive(getTestContext(), + mService.mPackageMonitor.onReceive(getTestContext(), genPackageDataClear(CALLING_PACKAGE_1, USER_10)); // Only manifest shortcuts will remain, and are no longer pinned. diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java index ffb2953d8383..5f246374b202 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java @@ -18,7 +18,6 @@ package com.android.server.pm; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; @@ -36,51 +35,6 @@ import java.util.List; */ @SmallTest public class ShortcutManagerTest6 extends BaseShortcutManagerTest { - - private static final String PACKAGE_SYSTEM_LAUNCHER = "com.android.systemlauncher"; - private static final String PACKAGE_SYSTEM_LAUNCHER_NAME = "systemlauncher_name"; - private static final int PACKAGE_SYSTEM_LAUNCHER_PRIORITY = 0; - - private static final String PACKAGE_FALLBACK_LAUNCHER = "com.android.settings"; - private static final String PACKAGE_FALLBACK_LAUNCHER_NAME = "fallback"; - private static final int PACKAGE_FALLBACK_LAUNCHER_PRIORITY = -999; - - private void prepareGetHomeActivitiesAsUser(ComponentName preferred, - List<ResolveInfo> candidates, int userId) { - doAnswer(inv -> { - ((List) inv.getArguments()[0]).addAll(candidates); - return preferred; - }).when(mMockPackageManagerInternal).getHomeActivitiesAsUser(any(List.class), eq(userId)); - } - - private static ComponentName cn(String packageName, String name) { - return new ComponentName(packageName, name); - } - - private static ResolveInfo ri(String packageName, String name, boolean isSystem, int priority) { - final ResolveInfo ri = new ResolveInfo(); - ri.activityInfo = new ActivityInfo(); - ri.activityInfo.applicationInfo = new ApplicationInfo(); - - ri.activityInfo.packageName = packageName; - ri.activityInfo.name = name; - if (isSystem) { - ri.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; - } - ri.priority = priority; - return ri; - } - - private static ResolveInfo getSystemLauncher() { - return ri(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME, true, - PACKAGE_SYSTEM_LAUNCHER_PRIORITY); - } - - private static ResolveInfo getFallbackLauncher() { - return ri(PACKAGE_FALLBACK_LAUNCHER, PACKAGE_FALLBACK_LAUNCHER_NAME, true, - PACKAGE_FALLBACK_LAUNCHER_PRIORITY); - } - public void testHasShortcutHostPermissionInner_systemLauncherOnly() { // Preferred isn't set, use the system launcher. prepareGetHomeActivitiesAsUser( diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java new file mode 100644 index 000000000000..f9ff5149e54c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2016 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.server.pm; + +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertContains; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertSuccess; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.readAll; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resultContains; + +import android.content.ComponentName; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.frameworks.servicestests.R; +import com.android.server.pm.ShortcutService.ConfigConstants; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Unit test for "cmd shortcut" + * + * Launcher related commands are tested in + */ +@SmallTest +public class ShortcutManagerTest7 extends BaseShortcutManagerTest { + private List<String> callShellCommand(String... args) throws IOException, RemoteException { + + // For reset to work, the current time needs to be incrementing. + mInjectedCurrentTimeMillis++; + + final AtomicInteger resultCode = new AtomicInteger(Integer.MIN_VALUE); + + final ResultReceiver rr = new ResultReceiver(mHandler) { + @Override + public void send(int resultCode_, Bundle resultData) { + resultCode.set(resultCode_); + } + }; + final File out = File.createTempFile("shellout-", ".tmp", + getTestContext().getCacheDir()); + try { + try (final ParcelFileDescriptor fd = ParcelFileDescriptor.open(out, + ParcelFileDescriptor.MODE_READ_WRITE)) { + mService.onShellCommand( + /* fdin*/ null, + /* fdout*/ fd.getFileDescriptor(), + /* fderr*/ fd.getFileDescriptor(), + args, rr); + } + return readAll(out); + } finally { + out.delete(); + } + } + + public void testNonShell() throws Exception { + mService.mMaxUpdatesPerInterval = 99; + + mInjectedCallingUid = 12345; + assertExpectException(SecurityException.class, "must be shell", + () -> callShellCommand("reset-config")); + + mInjectedCallingUid = Process.SYSTEM_UID; + assertExpectException(SecurityException.class, "must be shell", + () -> callShellCommand("reset-config")); + + assertEquals(99, mService.mMaxUpdatesPerInterval); + } + + public void testRoot() throws Exception { + mService.mMaxUpdatesPerInterval = 99; + + mInjectedCallingUid = Process.ROOT_UID; + assertSuccess(callShellCommand("reset-config")); + + assertEquals(3, mService.mMaxUpdatesPerInterval); + } + + public void testRestConfig() throws Exception { + mService.mMaxUpdatesPerInterval = 99; + + mInjectedCallingUid = Process.SHELL_UID; + assertSuccess(callShellCommand("reset-config")); + + assertEquals(3, mService.mMaxUpdatesPerInterval); + } + + public void testOverrideConfig() throws Exception { + mService.mMaxUpdatesPerInterval = 99; + + mInjectedCallingUid = Process.SHELL_UID; + assertSuccess(callShellCommand("override-config", + ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=1")); + + assertEquals(1, mService.mMaxUpdatesPerInterval); + } + + public void testResetThrottling() throws Exception { + prepareCrossProfileDataSet(); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + + mInjectedCallingUid = Process.SHELL_UID; + assertSuccess(callShellCommand("reset-throttling")); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertEquals(3, mManager.getRemainingCallCount()); + }); + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + } + + public void testResetThrottling_user_not_running() throws Exception { + prepareCrossProfileDataSet(); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + + mInjectedCallingUid = Process.SHELL_UID; + + assertTrue(resultContains( + callShellCommand("reset-throttling", "--user", "10"), + "User 10 is not running or locked")); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + } + + public void testResetThrottling_user_running() throws Exception { + prepareCrossProfileDataSet(); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + + mRunningUsers.put(USER_10, true); + mUnlockedUsers.put(USER_10, true); + + mInjectedCallingUid = Process.SHELL_UID; + assertSuccess(callShellCommand("reset-throttling", "--user", "10")); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertEquals(3, mManager.getRemainingCallCount()); + }); + } + + public void testResetAllThrottling() throws Exception { + prepareCrossProfileDataSet(); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertTrue(mManager.getRemainingCallCount() < 3); + }); + + mInjectedCallingUid = Process.SHELL_UID; + assertSuccess(callShellCommand("reset-all-throttling")); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertEquals(3, mManager.getRemainingCallCount()); + }); + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertEquals(3, mManager.getRemainingCallCount()); + }); + } + + public void testLauncherCommands() throws Exception { + prepareGetHomeActivitiesAsUser( + /* preferred */ null, + list(getSystemLauncher(), getFallbackLauncher()), + USER_0); + + prepareGetHomeActivitiesAsUser( + /* preferred */ cn(CALLING_PACKAGE_2, "name"), + list(getSystemLauncher(), getFallbackLauncher(), + ri(CALLING_PACKAGE_1, "name", false, 0), + ri(CALLING_PACKAGE_2, "name", false, 0) + ), + USER_10); + + assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0)); + + // First, test "get". + + mRunningUsers.put(USER_10, true); + mUnlockedUsers.put(USER_10, true); + mInjectedCallingUid = Process.SHELL_UID; + assertContains( + assertSuccess(callShellCommand("get-default-launcher")), + "Launcher: ComponentInfo{com.android.systemlauncher/systemlauncher_name}"); + + assertContains( + assertSuccess(callShellCommand("get-default-launcher", "--user", "10")), + "Launcher: ComponentInfo{com.android.test.2/name}"); + + // Next, test "clear". + assertSuccess(callShellCommand("clear-default-launcher", "--user", "10")); + + // User-10's launcher should be cleared. + assertEquals(null, mService.getUserShortcutsLocked(USER_10).getLastKnownLauncher()); + assertEquals(null, mService.getUserShortcutsLocked(USER_10).getCachedLauncher()); + + // but user'0's shouldn't. + assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME), + mService.getUserShortcutsLocked(USER_0).getCachedLauncher()); + + // Change user-0's launcher. + prepareGetHomeActivitiesAsUser( + /* preferred */ cn(CALLING_PACKAGE_1, "name"), + list( + ri(CALLING_PACKAGE_1, "name", false, 0) + ), + USER_0); + assertContains( + assertSuccess(callShellCommand("get-default-launcher")), + "Launcher: ComponentInfo{com.android.test.1/name}"); + } + + public void testUnloadUser() throws Exception { + prepareCrossProfileDataSet(); + + assertNotNull(mService.getShortcutsForTest().get(USER_10)); + + mRunningUsers.put(USER_10, true); + mUnlockedUsers.put(USER_10, true); + + mInjectedCallingUid = Process.SHELL_UID; + assertSuccess(callShellCommand("unload-user", "--user", "10")); + + assertNull(mService.getShortcutsForTest().get(USER_10)); + } + + public void testClearShortcuts() throws Exception { + + mRunningUsers.put(USER_10, true); + + // Add two manifests and two dynamics. + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), + R.xml.shortcut_2); + updatePackageVersion(CALLING_PACKAGE_1, 1); + mService.mPackageMonitor.onReceive(getTestContext(), + genPackageAddIntent(CALLING_PACKAGE_1, USER_10)); + + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertTrue(mManager.addDynamicShortcuts(list( + makeShortcut("s1"), makeShortcut("s2")))); + }); + runWithCaller(LAUNCHER_1, USER_10, () -> { + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_10); + }); + + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1", "ms2", "s1", "s2") + .areAllEnabled() + + .selectPinned() + .haveIds("ms2", "s2"); + }); + + // First, call for a different package. + + mRunningUsers.put(USER_10, true); + mUnlockedUsers.put(USER_10, true); + + mInjectedCallingUid = Process.SHELL_UID; + assertSuccess(callShellCommand("clear-shortcuts", "--user", "10", CALLING_PACKAGE_2)); + + // Shouldn't be cleared yet. + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1", "ms2", "s1", "s2") + .areAllEnabled() + + .selectPinned() + .haveIds("ms2", "s2"); + }); + + mInjectedCallingUid = Process.SHELL_UID; + assertSuccess(callShellCommand("clear-shortcuts", "--user", "10", CALLING_PACKAGE_1)); + + // Only manifest shortcuts will remain, and are no longer pinned. + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1", "ms2") + .areAllEnabled() + .areAllNotPinned(); + }); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java index e2dce8530ee6..11f9ebb52f44 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java @@ -16,15 +16,14 @@ package com.android.server.pm; -import android.os.UserHandle; -import com.android.server.devicepolicy.DpmTestUtils; - import android.os.Bundle; +import android.os.UserHandle; import android.os.UserManager; import android.test.AndroidTestCase; -import android.test.MoreAsserts; import android.test.suitebuilder.annotation.SmallTest; +import com.android.server.devicepolicy.DpmTestUtils; + /** * Tests for {@link com.android.server.pm.UserRestrictionsUtils}. * diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java index ce7adfa70878..5d29363aea13 100644 --- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java +++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java @@ -59,6 +59,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; @@ -93,7 +95,12 @@ public class ShortcutManagerTestUtils { private ShortcutManagerTestUtils() { } - private static List<String> readAll(ParcelFileDescriptor pfd) { + public static List<String> readAll(File file) throws FileNotFoundException { + return readAll(ParcelFileDescriptor.open( + file.getAbsoluteFile(), ParcelFileDescriptor.MODE_READ_ONLY)); + } + + public static List<String> readAll(ParcelFileDescriptor pfd) { try { try { final ArrayList<String> ret = new ArrayList<>(); @@ -114,7 +121,7 @@ public class ShortcutManagerTestUtils { } } - private static String concatResult(List<String> result) { + public static String concatResult(List<String> result) { final StringBuilder sb = new StringBuilder(); for (String s : result) { sb.append(s); @@ -123,6 +130,30 @@ public class ShortcutManagerTestUtils { return sb.toString(); } + public static boolean resultContains(List<String> result, String expected) { + for (String line : result) { + if (line.contains(expected)) { + return true; + } + } + return false; + } + + public static List<String> assertSuccess(List<String> result) { + if (!resultContains(result, "Success")) { + fail("Command failed. Result was:\n" + concatResult(result)); + } + return result; + } + + public static List<String> assertContains(List<String> result, String expected) { + if (!resultContains(result, expected)) { + fail("Didn't contain expected string=" + expected + + "\nActual:\n" + concatResult(result)); + } + return result; + } + private static List<String> runCommand(Instrumentation instrumentation, String command) { return runCommand(instrumentation, command, null); } @@ -321,24 +352,29 @@ public class ShortcutManagerTestUtils { return filter(list, si -> si.getLastChangedTimestamp() >= time); } + @FunctionalInterface + public interface ExceptionRunnable { + void run() throws Exception; + } + public static void assertExpectException(Class<? extends Throwable> expectedExceptionType, - String expectedExceptionMessageRegex, Runnable r) { + String expectedExceptionMessageRegex, ExceptionRunnable r) { assertExpectException("", expectedExceptionType, expectedExceptionMessageRegex, r); } public static void assertCannotUpdateImmutable(Runnable r) { assertExpectException( - IllegalArgumentException.class, "may not be manipulated via APIs", r); + IllegalArgumentException.class, "may not be manipulated via APIs", r::run); } public static void assertDynamicShortcutCountExceeded(Runnable r) { assertExpectException(IllegalArgumentException.class, - "Max number of dynamic shortcuts exceeded", r); + "Max number of dynamic shortcuts exceeded", r::run); } public static void assertExpectException(String message, Class<? extends Throwable> expectedExceptionType, - String expectedExceptionMessageRegex, Runnable r) { + String expectedExceptionMessageRegex, ExceptionRunnable r) { try { r.run(); } catch (Throwable e) { |