diff options
373 files changed, 10049 insertions, 4562 deletions
diff --git a/ApiDocs.bp b/ApiDocs.bp index 029699efe3b9..ca921ff97c35 100644 --- a/ApiDocs.bp +++ b/ApiDocs.bp @@ -150,12 +150,10 @@ doc_defaults { ":current-support-api", ":current-androidx-api", ], - create_stubs: false, } doc_defaults { name: "framework-dokka-docs-default", - create_stubs: false, } droiddoc { diff --git a/apct-tests/perftests/autofill/Android.bp b/apct-tests/perftests/autofill/Android.bp index 9ac8c87d3de0..4f6c21af9281 100644 --- a/apct-tests/perftests/autofill/Android.bp +++ b/apct-tests/perftests/autofill/Android.bp @@ -20,8 +20,9 @@ android_test { "androidx.test.rules", "androidx.annotation_annotation", "apct-perftests-utils", - "collector-device-lib-platform", + "collector-device-lib", ], platform_apis: true, test_suites: ["device-tests"], + data: [":perfetto_artifacts"], } diff --git a/apct-tests/perftests/autofill/AndroidManifest.xml b/apct-tests/perftests/autofill/AndroidManifest.xml index 51f6a76b2782..57595a213d20 100644 --- a/apct-tests/perftests/autofill/AndroidManifest.xml +++ b/apct-tests/perftests/autofill/AndroidManifest.xml @@ -16,11 +16,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.perftests.autofill"> - <uses-sdk android:targetSdkVersion="28" /> - - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - <uses-permission android:name="android.permission.REAL_GET_TASKS" /> - <application> <uses-library android:name="android.test.runner" /> <activity android:name="android.perftests.utils.PerfTestActivity" diff --git a/apct-tests/perftests/autofill/AndroidTest.xml b/apct-tests/perftests/autofill/AndroidTest.xml index 29f9f94bcac7..eee7bdc3e330 100644 --- a/apct-tests/perftests/autofill/AndroidTest.xml +++ b/apct-tests/perftests/autofill/AndroidTest.xml @@ -21,8 +21,40 @@ <option name="test-file-name" value="AutofillPerfTests.apk" /> </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" /> + </target_preparer> + + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path" /> + </metrics_collector> + + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.perftests.autofill" /> <option name="hidden-api-checks" value="false"/> + + <!-- Listener related args for collecting the traces and waiting for the device to stabilize. --> + <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" /> + <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. --> + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> + + <!-- ProcLoadListener related arguments --> + <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run --> + <option name="instrumentation-arg" key="procload-collector:per_run" value="true" /> + <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" /> + <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" /> + <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" /> + + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" /> + <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" /> + + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> </test> </configuration> diff --git a/apct-tests/perftests/textclassifier/Android.bp b/apct-tests/perftests/textclassifier/Android.bp index 49952dc1d009..c40e0252cb7e 100644 --- a/apct-tests/perftests/textclassifier/Android.bp +++ b/apct-tests/perftests/textclassifier/Android.bp @@ -19,7 +19,9 @@ android_test { "androidx.test.rules", "androidx.annotation_annotation", "apct-perftests-utils", + "collector-device-lib", ], + data: [":perfetto_artifacts"], platform_apis: true, test_suites: ["device-tests"], } diff --git a/apct-tests/perftests/textclassifier/AndroidTest.xml b/apct-tests/perftests/textclassifier/AndroidTest.xml index 3df51b8ae67a..3fac462448f0 100644 --- a/apct-tests/perftests/textclassifier/AndroidTest.xml +++ b/apct-tests/perftests/textclassifier/AndroidTest.xml @@ -21,8 +21,40 @@ <option name="test-file-name" value="TextClassifierPerfTests.apk" /> </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" /> + </target_preparer> + + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path" /> + </metrics_collector> + + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.perftests.textclassifier" /> <option name="hidden-api-checks" value="false"/> + + <!-- Listener related args for collecting the traces and waiting for the device to stabilize. --> + <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" /> + <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. --> + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> + + <!-- ProcLoadListener related arguments --> + <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run --> + <option name="instrumentation-arg" key="procload-collector:per_run" value="true" /> + <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" /> + <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" /> + <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" /> + + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" /> + <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" /> + + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> </test> </configuration> diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassifierPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassifierPerfTest.java index 14a121d60c2e..324def83785f 100644 --- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassifierPerfTest.java +++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassifierPerfTest.java @@ -18,6 +18,7 @@ package android.view.textclassifier; import android.content.Context; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; +import android.service.textclassifier.TextClassifierService; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; @@ -25,86 +26,85 @@ import androidx.test.filters.LargeTest; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.Random; -@RunWith(Parameterized.class) @LargeTest public class TextClassifierPerfTest { - /** Request contains meaning text, rather than garbled text. */ - private static final int ACTUAL_REQUEST = 0; - private static final String RANDOM_CHAR_SET = "abcdefghijklmnopqrstuvwxyz0123456789"; + private static final String TEXT = " Oh hi Mark, the number is (323) 654-6192.\n" + + "Anyway, I'll meet you at 1600 Pennsylvania Avenue NW.\n" + + "My flight is LX 38 and I'll arrive at 8:00pm.\n" + + "Also, check out www.google.com.\n"; @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); - @Parameterized.Parameters(name = "size{0}") - public static Collection<Object[]> data() { - return Arrays.asList(new Object[][]{{ACTUAL_REQUEST}, {10}, {100}, {1000}}); - } - private TextClassifier mTextClassifier; - private final int mSize; - - public TextClassifierPerfTest(int size) { - mSize = size; - } @Before public void setUp() { Context context = InstrumentationRegistry.getTargetContext(); - TextClassificationManager textClassificationManager = - context.getSystemService(TextClassificationManager.class); - mTextClassifier = textClassificationManager.getTextClassifier(TextClassifier.LOCAL); + mTextClassifier = TextClassifierService.getDefaultTextClassifierImplementation(context); } @Test - public void testSuggestConversationActions() { - String text = mSize == ACTUAL_REQUEST ? "Where are you?" : generateRandomString(mSize); - ConversationActions.Request request = createConversationActionsRequest(text); + public void testClassifyText() { + TextClassification.Request request = + new TextClassification.Request.Builder(TEXT, 0, TEXT.length()).build(); BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { - mTextClassifier.suggestConversationActions(request); + mTextClassifier.classifyText(request); } } @Test - public void testDetectLanguage() { - String text = mSize == ACTUAL_REQUEST - ? "これは日本語のテキストです" : generateRandomString(mSize); - TextLanguage.Request request = createTextLanguageRequest(text); + public void testSuggestSelection() { + // Trying to select the phone number. + TextSelection.Request request = + new TextSelection.Request.Builder( + TEXT, + /* startIndex= */ 28, + /* endIndex= */29) + .build(); BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { - mTextClassifier.detectLanguage(request); + mTextClassifier.suggestSelection(request); } } - private static ConversationActions.Request createConversationActionsRequest(CharSequence text) { + @Test + public void testGenerateLinks() { + TextLinks.Request request = + new TextLinks.Request.Builder(TEXT).build(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mTextClassifier.generateLinks(request); + } + } + + @Test + public void testSuggestConversationActions() { ConversationActions.Message message = new ConversationActions.Message.Builder( ConversationActions.Message.PERSON_USER_OTHERS) - .setText(text) + .setText(TEXT) .build(); - return new ConversationActions.Request.Builder(Collections.singletonList(message)) + ConversationActions.Request request = new ConversationActions.Request.Builder( + Collections.singletonList(message)) .build(); - } - private static TextLanguage.Request createTextLanguageRequest(CharSequence text) { - return new TextLanguage.Request.Builder(text).build(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mTextClassifier.suggestConversationActions(request); + } } - private static String generateRandomString(int length) { - Random random = new Random(); - StringBuilder stringBuilder = new StringBuilder(length); - for (int i = 0; i < length; i++) { - int index = random.nextInt(RANDOM_CHAR_SET.length()); - stringBuilder.append(RANDOM_CHAR_SET.charAt(index)); + @Test + public void testDetectLanguage() { + TextLanguage.Request request = new TextLanguage.Request.Builder(TEXT).build(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mTextClassifier.detectLanguage(request); } - return stringBuilder.toString(); } } diff --git a/api/current.txt b/api/current.txt index 09de005c008c..b70103b931d4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11900,6 +11900,7 @@ package android.content.pm { field public static final int INVALID_ID = -1; // 0xffffffff field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2 field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0 + field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4 field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3 field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1 } diff --git a/api/system-current.txt b/api/system-current.txt index fb0d924673b8..56059dd9d864 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4201,6 +4201,7 @@ package android.media { method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups(); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes); method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); @@ -4219,6 +4220,7 @@ package android.media { method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int requestAudioFocus(@NonNull android.media.AudioFocusRequest, @Nullable android.media.audiopolicy.AudioPolicy); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long); method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]); @@ -4229,6 +4231,11 @@ package android.media { field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1 field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4 field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2 + field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3 + field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4 + field public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2; // 0x2 + field public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1; // 0x1 + field public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; // 0x0 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int STREAM_ASSISTANT = 11; // 0xb field public static final int SUCCESS = 0; // 0x0 } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 36ff20f45ec2..9c796128df84 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -417,7 +417,7 @@ status_t BootAnimation::readyToRun() { // this guest property specifies multi-display IDs to show the boot animation // multiple ids can be set with comma (,) as separator, for example: // setprop persist.boot.animation.displays 19260422155234049,19261083906282754 - Vector<uint64_t> physicalDisplayIds; + Vector<PhysicalDisplayId> physicalDisplayIds; char displayValue[PROPERTY_VALUE_MAX] = ""; property_get(DISPLAYS_PROP_NAME, displayValue, ""); bool isValid = displayValue[0] != '\0'; @@ -435,7 +435,7 @@ status_t BootAnimation::readyToRun() { } if (isValid) { std::istringstream stream(displayValue); - for (PhysicalDisplayId id; stream >> id; ) { + for (PhysicalDisplayId id; stream >> id.value; ) { physicalDisplayIds.add(id); if (stream.peek() == ',') stream.ignore(); diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index a46a54cc2063..dec4a567fc81 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -51,12 +51,11 @@ static void usage(const char* pname, PhysicalDisplayId displayId) "usage: %s [-hp] [-d display-id] [FILENAME]\n" " -h: this message\n" " -p: save the file as a png.\n" - " -d: specify the physical display ID to capture (default: %" - ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ")\n" + " -d: specify the physical display ID to capture (default: %s)\n" " see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n" "If FILENAME ends with .png it will be saved as a png.\n" "If FILENAME is not given, the results will be printed to stdout.\n", - pname, displayId); + pname, to_string(displayId).c_str()); } static int32_t flinger2bitmapFormat(PixelFormat f) @@ -137,7 +136,7 @@ int main(int argc, char** argv) png = true; break; case 'd': - displayId = atoll(optarg); + displayId = PhysicalDisplayId(atoll(optarg)); break; case '?': case 'h': @@ -183,7 +182,7 @@ int main(int argc, char** argv) ProcessState::self()->startThreadPool(); ScreenCaptureResults captureResults; - status_t result = ScreenshotClient::captureDisplay(*displayId, captureResults); + status_t result = ScreenshotClient::captureDisplay(displayId->value, captureResults); if (result != NO_ERROR) { close(fd); return 1; diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp index 04b00cb837b2..14b74da0c616 100644 --- a/cmds/uiautomator/library/Android.bp +++ b/cmds/uiautomator/library/Android.bp @@ -54,7 +54,6 @@ droiddoc { ], installable: false, custom_template: "droiddoc-templates-sdk", - create_stubs: false, } java_library_static { @@ -66,6 +65,7 @@ java_library_static { "android.test.runner", "junit", ], + java_version: "1.8", } java_library_static { diff --git a/config/hiddenapi-force-blacklist.txt b/config/hiddenapi-force-blocked.txt index b328f2ac1955..b328f2ac1955 100644 --- a/config/hiddenapi-force-blacklist.txt +++ b/config/hiddenapi-force-blocked.txt diff --git a/config/hiddenapi-greylist-max-o.txt b/config/hiddenapi-max-target-o.txt index 023bf3876228..023bf3876228 100644 --- a/config/hiddenapi-greylist-max-o.txt +++ b/config/hiddenapi-max-target-o.txt diff --git a/config/hiddenapi-greylist-max-p.txt b/config/hiddenapi-max-target-p.txt index 351e71dd9538..351e71dd9538 100644 --- a/config/hiddenapi-greylist-max-p.txt +++ b/config/hiddenapi-max-target-p.txt diff --git a/config/hiddenapi-greylist-max-q.txt b/config/hiddenapi-max-target-q.txt index 4832dd184ec5..4832dd184ec5 100644 --- a/config/hiddenapi-greylist-max-q.txt +++ b/config/hiddenapi-max-target-q.txt diff --git a/config/hiddenapi-greylist-packages.txt b/config/hiddenapi-unsupported-packages.txt index 986d2591a007..986d2591a007 100644 --- a/config/hiddenapi-greylist-packages.txt +++ b/config/hiddenapi-unsupported-packages.txt diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-unsupported.txt index a3543dc7f4ee..a3543dc7f4ee 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-unsupported.txt diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 2be92826cf38..522b8cc5a46f 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2880,7 +2880,7 @@ public class Activity extends ContextThemeWrapper * Return the number of actions that will be displayed in the picture-in-picture UI when the * user interacts with the activity currently in picture-in-picture mode. This number may change * if the global configuration changes (ie. if the device is plugged into an external display), - * but will always be larger than three. + * but will always be at least three. */ public int getMaxNumPictureInPictureActions() { try { diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 9cf0d2a3a36a..1f8cf8ac6d1d 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -341,7 +341,8 @@ public abstract class ActivityManagerInternal { /** @see com.android.server.am.ActivityManagerService#monitor */ public abstract void monitor(); - /** Input dispatch timeout to a window, start the ANR process. */ + /** Input dispatch timeout to a window, start the ANR process. Return the timeout extension, + * in milliseconds, or 0 to abort dispatch. */ public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason); public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName, ApplicationInfo aInfo, String parentShortComponentName, Object parentProc, diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index bca6f39e1ded..54f3f1026050 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -215,7 +215,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { private long mMisses = 0; @GuardedBy("mLock") - private long mMissDisabled[] = new long[]{ 0, 0, 0 }; + private long mSkips[] = new long[]{ 0, 0, 0 }; @GuardedBy("mLock") private long mMissOverflow = 0; @@ -223,6 +223,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> { @GuardedBy("mLock") private long mHighWaterMark = 0; + @GuardedBy("mLock") + private long mClears = 0; + // Most invalidation is done in a static context, so the counters need to be accessible. @GuardedBy("sCorkLock") private static final HashMap<String, Long> sInvalidates = new HashMap<>(); @@ -273,6 +276,13 @@ public abstract class PropertyInvalidatedCache<Query, Result> { */ private volatile SystemProperties.Handle mPropertyHandle; + /** + * The name by which this cache is known. This should normally be the + * binder call that is being cached, but the constructors default it to + * the property name. + */ + private final String mCacheName; + @GuardedBy("mLock") private final LinkedHashMap<Query, Result> mCache; @@ -297,9 +307,23 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * * @param maxEntries Maximum number of entries to cache; LRU discard * @param propertyName Name of the system property holding the cache invalidation nonce + * Defaults the cache name to the property name. */ public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) { + this(maxEntries, propertyName, propertyName); + } + + /** + * Make a new property invalidated cache. + * + * @param maxEntries Maximum number of entries to cache; LRU discard + * @param propertyName Name of the system property holding the cache invalidation nonce + * @param cacheName Name of this cache in debug and dumpsys + */ + public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName, + @NonNull String cacheName) { mPropertyName = propertyName; + mCacheName = cacheName; mMaxEntries = maxEntries; mCache = new LinkedHashMap<Query, Result>( 2 /* start small */, @@ -320,7 +344,6 @@ public abstract class PropertyInvalidatedCache<Query, Result> { }; synchronized (sCorkLock) { sCaches.put(this, null); - sInvalidates.put(propertyName, (long) 0); } } @@ -333,6 +356,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { Log.d(TAG, "clearing cache for " + mPropertyName); } mCache.clear(); + mClears++; } } @@ -413,7 +437,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { // Do not bother collecting statistics if the cache is // locally disabled. synchronized (mLock) { - mMissDisabled[(int) currentNonce]++; + mSkips[(int) currentNonce]++; } } @@ -742,12 +766,16 @@ public abstract class PropertyInvalidatedCache<Query, Result> { boolean alreadyQueued = mUncorkDeadlineMs >= 0; if (DEBUG) { Log.w(TAG, String.format( - "autoCork mUncorkDeadlineMs=%s", mUncorkDeadlineMs)); + "autoCork %s mUncorkDeadlineMs=%s", mPropertyName, + mUncorkDeadlineMs)); } mUncorkDeadlineMs = SystemClock.uptimeMillis() + mAutoCorkDelayMs; if (!alreadyQueued) { getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs); PropertyInvalidatedCache.corkInvalidations(mPropertyName); + } else { + final long count = sCorkedInvalidates.getOrDefault(mPropertyName, (long) 0); + sCorkedInvalidates.put(mPropertyName, count + 1); } } } @@ -756,7 +784,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> { synchronized (mLock) { if (DEBUG) { Log.w(TAG, String.format( - "handleMsesage mUncorkDeadlineMs=%s", mUncorkDeadlineMs)); + "handleMsesage %s mUncorkDeadlineMs=%s", + mPropertyName, mUncorkDeadlineMs)); } if (mUncorkDeadlineMs < 0) { @@ -816,7 +845,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * method is public so clients can use it. */ public String cacheName() { - return mPropertyName; + return mCacheName; } /** @@ -864,16 +893,20 @@ public abstract class PropertyInvalidatedCache<Query, Result> { } synchronized (mLock) { - pw.println(String.format(" Cache Property Name: %s", cacheName())); - pw.println(String.format(" Hits: %d, Misses: %d, Invalidates: %d, Overflows: %d", - mHits, mMisses, invalidateCount, mMissOverflow)); - pw.println(String.format(" Miss-corked: %d, Miss-unset: %d, Miss-other: %d," + - " CorkedInvalidates: %d", - mMissDisabled[NONCE_CORKED], mMissDisabled[NONCE_UNSET], - mMissDisabled[NONCE_DISABLED], corkedInvalidates)); - pw.println(String.format(" Last Observed Nonce: %d", mLastSeenNonce)); - pw.println(String.format(" Current Size: %d, Max Size: %d, HW Mark: %d", - mCache.size(), mMaxEntries, mHighWaterMark)); + pw.println(String.format(" Cache Name: %s", cacheName())); + pw.println(String.format(" Property: %s", mPropertyName)); + final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED]; + pw.println(String.format(" Hits: %d, Misses: %d, Skips: %d, Clears: %d", + mHits, mMisses, skips, mClears)); + pw.println(String.format(" Skip-corked: %d, Skip-unset: %d, Skip-other: %d", + mSkips[NONCE_CORKED], mSkips[NONCE_UNSET], + mSkips[NONCE_DISABLED])); + pw.println(String.format( + " Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d", + mLastSeenNonce, invalidateCount, corkedInvalidates)); + pw.println(String.format( + " Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d", + mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow)); pw.println(String.format(" Enabled: %s", mDisabled ? "false" : "true")); Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet(); diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 7c6eff143724..06d1b74abc86 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -510,6 +510,9 @@ public class UiModeManager { } /** + * Activating night mode for the current user + * + * @return {@code true} if the change is successful * @hide */ public boolean setNightModeActivated(boolean active) { diff --git a/core/java/android/app/people/ConversationChannel.java b/core/java/android/app/people/ConversationChannel.java new file mode 100644 index 000000000000..39c5c85e456c --- /dev/null +++ b/core/java/android/app/people/ConversationChannel.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 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 android.app.people; + +import android.app.NotificationChannel; +import android.content.pm.ShortcutInfo; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The non-customized notification channel of a conversation. It contains the information to render + * the conversation and allows the user to open and customize the conversation setting. + * + * @hide + */ +public final class ConversationChannel implements Parcelable { + + private ShortcutInfo mShortcutInfo; + private NotificationChannel mParentNotificationChannel; + private long mLastEventTimestamp; + private boolean mHasActiveNotifications; + + public static final Creator<ConversationChannel> CREATOR = new Creator<ConversationChannel>() { + @Override + public ConversationChannel createFromParcel(Parcel in) { + return new ConversationChannel(in); + } + + @Override + public ConversationChannel[] newArray(int size) { + return new ConversationChannel[size]; + } + }; + + public ConversationChannel(ShortcutInfo shortcutInfo, + NotificationChannel parentNotificationChannel, long lastEventTimestamp, + boolean hasActiveNotifications) { + mShortcutInfo = shortcutInfo; + mParentNotificationChannel = parentNotificationChannel; + mLastEventTimestamp = lastEventTimestamp; + mHasActiveNotifications = hasActiveNotifications; + } + + public ConversationChannel(Parcel in) { + mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader()); + mParentNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader()); + mLastEventTimestamp = in.readLong(); + mHasActiveNotifications = in.readBoolean(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mShortcutInfo, flags); + dest.writeParcelable(mParentNotificationChannel, flags); + dest.writeLong(mLastEventTimestamp); + dest.writeBoolean(mHasActiveNotifications); + } + + public ShortcutInfo getShortcutInfo() { + return mShortcutInfo; + } + + public NotificationChannel getParentNotificationChannel() { + return mParentNotificationChannel; + } + + public long getLastEventTimestamp() { + return mLastEventTimestamp; + } + + /** + * Whether this conversation has any active notifications. If it's true, the shortcut for this + * conversation can't be uncached until all its active notifications are dismissed. + */ + public boolean hasActiveNotifications() { + return mHasActiveNotifications; + } +} diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl new file mode 100644 index 000000000000..61dac0d64422 --- /dev/null +++ b/core/java/android/app/people/IPeopleManager.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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 android.app.people; + +import android.content.pm.ParceledListSlice; +import android.net.Uri; +import android.os.IBinder; + +/** + * System private API for talking with the people service. + * {@hide} + */ +interface IPeopleManager { + /** + * Returns the recent conversations. The conversations that have customized notification + * settings are excluded from the returned list. + */ + ParceledListSlice getRecentConversations(); + + /** + * Removes the specified conversation from the recent conversations list and uncaches the + * shortcut associated with the conversation. + */ + void removeRecentConversation(in String packageName, int userId, in String shortcutId); + + /** Removes all the recent conversations and uncaches their cached shortcuts. */ + void removeAllRecentConversations(); +} diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java index 6bd365fad6f6..0770aff4e9bb 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java @@ -117,7 +117,7 @@ public final class TimeZoneDetectorImpl implements TimeZoneDetector { } private void notifyConfigurationListeners(@NonNull TimeZoneConfiguration configuration) { - ArraySet<TimeZoneConfigurationListener> configurationListeners; + final ArraySet<TimeZoneConfigurationListener> configurationListeners; synchronized (this) { configurationListeners = new ArraySet<>(mConfigurationListeners); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 16cdf2334ad8..52b04675b7a5 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3496,6 +3496,7 @@ public abstract class Context { //@hide: TIME_ZONE_DETECTOR_SERVICE, PERMISSION_SERVICE, LIGHTS_SERVICE, + //@hide: PEOPLE_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -5189,6 +5190,14 @@ public abstract class Context { public static final String SMS_SERVICE = "sms"; /** + * Use with {@link #getSystemService(String)} to access people service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String PEOPLE_SERVICE = "people"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index bd02210259b8..31c77eeb5424 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -963,7 +963,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { /** @hide */ public static final int LOCK_TASK_LAUNCH_MODE_ALWAYS = 2; /** @hide */ - public static final int LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED = 3; + public static final int LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED = 3; /** @hide */ public static final String lockTaskLaunchModeToString(int lockTaskLaunchMode) { @@ -974,8 +974,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { return "LOCK_TASK_LAUNCH_MODE_NEVER"; case LOCK_TASK_LAUNCH_MODE_ALWAYS: return "LOCK_TASK_LAUNCH_MODE_ALWAYS"; - case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: - return "LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED"; + case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED: + return "LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED"; default: return "unknown=" + lockTaskLaunchMode; } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index df9db278e095..bed7b26034e5 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -2075,7 +2075,8 @@ public class PackageInstaller { STAGED_SESSION_NO_ERROR, STAGED_SESSION_VERIFICATION_FAILED, STAGED_SESSION_ACTIVATION_FAILED, - STAGED_SESSION_UNKNOWN}) + STAGED_SESSION_UNKNOWN, + STAGED_SESSION_OTHER_ERROR}) @Retention(RetentionPolicy.SOURCE) public @interface StagedSessionErrorCode{} /** @@ -2101,6 +2102,12 @@ public class PackageInstaller { */ public static final int STAGED_SESSION_UNKNOWN = 3; + /** + * Constant indicating that a known error occurred while processing this staged session, but + * the error could not be matched to other categories. + */ + public static final int STAGED_SESSION_OTHER_ERROR = 4; + /** {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int sessionId; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 42a610700051..e08af5534afd 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -8089,7 +8089,8 @@ public abstract class PackageManager { private static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo> sApplicationInfoCache = new PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>( - 16, PermissionManager.CACHE_KEY_PACKAGE_INFO) { + 16, PermissionManager.CACHE_KEY_PACKAGE_INFO, + "getApplicationInfo") { @Override protected ApplicationInfo recompute(ApplicationInfoQuery query) { return getApplicationInfoAsUserUncached( @@ -8190,7 +8191,8 @@ public abstract class PackageManager { private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo> sPackageInfoCache = new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>( - 32, PermissionManager.CACHE_KEY_PACKAGE_INFO) { + 32, PermissionManager.CACHE_KEY_PACKAGE_INFO, + "getPackageInfo") { @Override protected PackageInfo recompute(PackageInfoQuery query) { return getPackageInfoAsUserUncached( diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index fbe6a5052f3d..b0d449769be4 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -743,6 +743,12 @@ public abstract class BatteryStats implements Parcelable { @UnsupportedAppUsage public abstract ArrayMap<String, ? extends Pkg> getPackageStats(); + /** + * Returns the proportion of power consumed by the System Service + * calls made by this UID. + */ + public abstract double getProportionalSystemServiceUsage(); + public abstract ControllerActivityCounter getWifiControllerActivity(); public abstract ControllerActivityCounter getBluetoothControllerActivity(); public abstract ControllerActivityCounter getModemControllerActivity(); @@ -2882,6 +2888,17 @@ public abstract class BatteryStats implements Parcelable { public abstract int getDischargeAmountScreenDozeSinceCharge(); /** + * Returns the approximate CPU time (in microseconds) spent by the system server handling + * incoming service calls from apps. + * + * @param cluster the index of the CPU cluster. + * @param step the index of the CPU speed. This is not the actual speed of the CPU. + * @see com.android.internal.os.PowerProfile#getNumCpuClusters() + * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int) + */ + public abstract long getSystemServiceTimeAtCpuSpeed(int cluster, int step); + + /** * Returns the total, last, or current battery uptime in microseconds. * * @param curTime the elapsed realtime in microseconds. diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index bf3d46fa4a44..0c190719af57 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -608,7 +608,7 @@ public final class PermissionManager { /** @hide */ private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache = new PropertyInvalidatedCache<PermissionQuery, Integer>( - 16, CACHE_KEY_PACKAGE_INFO) { + 16, CACHE_KEY_PACKAGE_INFO, "checkPermission") { @Override protected Integer recompute(PermissionQuery query) { return checkPermissionUncached(query.permission, query.pid, query.uid); @@ -689,7 +689,7 @@ public final class PermissionManager { private static PropertyInvalidatedCache<PackageNamePermissionQuery, Integer> sPackageNamePermissionCache = new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>( - 16, CACHE_KEY_PACKAGE_INFO) { + 16, CACHE_KEY_PACKAGE_INFO, "checkPackageNamePermission") { @Override protected Integer recompute(PackageNamePermissionQuery query) { return checkPackageNamePermissionUncached( diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 5acc11a868bf..660455e59a4a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7912,6 +7912,13 @@ public final class Settings { public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit"; /** + * Internal use, one handed mode tutorial showed times. + * @hide + */ + public static final String ONE_HANDED_TUTORIAL_SHOW_COUNT = + "one_handed_tutorial_show_count"; + + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per * UiModeManager. diff --git a/core/java/android/view/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java index 3d05e2a0b9f6..108345e6db0e 100644 --- a/core/java/android/view/InputApplicationHandle.java +++ b/core/java/android/view/InputApplicationHandle.java @@ -34,7 +34,7 @@ public final class InputApplicationHandle { public String name; // Dispatching timeout. - public long dispatchingTimeoutNanos; + public long dispatchingTimeoutMillis; public final IBinder token; @@ -46,7 +46,7 @@ public final class InputApplicationHandle { public InputApplicationHandle(InputApplicationHandle handle) { this.token = handle.token; - this.dispatchingTimeoutNanos = handle.dispatchingTimeoutNanos; + this.dispatchingTimeoutMillis = handle.dispatchingTimeoutMillis; this.name = handle.name; } diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index a7e0305f2c09..e341845277d4 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -50,7 +50,7 @@ public final class InputWindowHandle { public int layoutParamsType; // Dispatching timeout. - public long dispatchingTimeoutNanos; + public long dispatchingTimeoutMillis; // Window frame. public int frameLeft; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0818abeff923..89178217366f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -23358,7 +23358,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * displaying, else return the result of calling through to the * super class. * - * @return boolean If true than the Drawable is being displayed in the + * @return boolean If true then the Drawable is being displayed in the * view; else false and it is not allowed to animate. * * @see #unscheduleDrawable(android.graphics.drawable.Drawable) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 64ddb2f4d9d9..3f02d701f71f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1814,19 +1814,13 @@ public final class ViewRootImpl implements ViewParent, /** * Called after window layout to update the bounds surface. If the surface insets have changed * or the surface has resized, update the bounds surface. - * - * @param shouldReparent Whether it should reparent the bounds layer to the main SurfaceControl. */ - private void updateBoundsLayer(boolean shouldReparent) { + private void updateBoundsLayer() { if (mBoundsLayer != null) { setBoundsLayerCrop(); - mTransaction.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(), - mSurface.getNextFrameNumber()); - - if (shouldReparent) { - mTransaction.reparent(mBoundsLayer, getRenderSurfaceControl()); - } - mTransaction.apply(); + mTransaction.deferTransactionUntil(mBoundsLayer, + getRenderSurfaceControl(), mSurface.getNextFrameNumber()) + .apply(); } } @@ -2905,16 +2899,7 @@ public final class ViewRootImpl implements ViewParent, } if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) { - // If the surface has been replaced, there's a chance the bounds layer is not parented - // to the new layer. When updating bounds layer, also reparent to the main VRI - // SurfaceControl to ensure it's correctly placed in the hierarchy. - // - // This needs to be done on the client side since WMS won't reparent the children to the - // new surface if it thinks the app is closing. WMS gets the signal that the app is - // stopping, but on the client side it doesn't get stopped since it's restarted quick - // enough. WMS doesn't want to keep around old children since they will leak when the - // client creates new children. - updateBoundsLayer(surfaceReplaced); + updateBoundsLayer(); } final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index c4eb39626d8b..7683067958d8 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -385,6 +385,7 @@ public class Editor { private final SuggestionHelper mSuggestionHelper = new SuggestionHelper(); private boolean mFlagCursorDragFromAnywhereEnabled; + private float mCursorDragDirectionMinXYRatio; private boolean mFlagInsertionHandleGesturesEnabled; // Specifies whether the new magnifier (with fish-eye effect) is enabled. @@ -425,6 +426,11 @@ public class Editor { mFlagCursorDragFromAnywhereEnabled = AppGlobals.getIntCoreSetting( WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT ? 1 : 0) != 0; + final int cursorDragMinAngleFromVertical = AppGlobals.getIntCoreSetting( + WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, + WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT); + mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio( + cursorDragMinAngleFromVertical); mFlagInsertionHandleGesturesEnabled = AppGlobals.getIntCoreSetting( WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES, WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT ? 1 : 0) != 0; @@ -437,6 +443,8 @@ public class Editor { if (TextView.DEBUG_CURSOR) { logCursor("Editor", "Cursor drag from anywhere is %s.", mFlagCursorDragFromAnywhereEnabled ? "enabled" : "disabled"); + logCursor("Editor", "Cursor drag min angle from vertical is %d (= %f x/y ratio)", + cursorDragMinAngleFromVertical, mCursorDragDirectionMinXYRatio); logCursor("Editor", "Insertion handle gestures is %s.", mFlagInsertionHandleGesturesEnabled ? "enabled" : "disabled"); logCursor("Editor", "New magnifier is %s.", @@ -463,6 +471,11 @@ public class Editor { } @VisibleForTesting + public void setCursorDragMinAngleFromVertical(int degreesFromVertical) { + mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio(degreesFromVertical); + } + + @VisibleForTesting public boolean getFlagInsertionHandleGesturesEnabled() { return mFlagInsertionHandleGesturesEnabled; } @@ -6127,10 +6140,11 @@ public class Editor { if (mIsDraggingCursor) { performCursorDrag(event); } else if (mFlagCursorDragFromAnywhereEnabled - && mTextView.getLayout() != null - && mTextView.isFocused() - && mTouchState.isMovedEnoughForDrag() - && !mTouchState.isDragCloseToVertical()) { + && mTextView.getLayout() != null + && mTextView.isFocused() + && mTouchState.isMovedEnoughForDrag() + && (mTouchState.getInitialDragDirectionXYRatio() + > mCursorDragDirectionMinXYRatio || mTouchState.isOnHandle())) { startCursorDrag(event); } break; diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java index 9eb63087a66e..751436865ff5 100644 --- a/core/java/android/widget/EditorTouchState.java +++ b/core/java/android/widget/EditorTouchState.java @@ -59,7 +59,7 @@ public class EditorTouchState { private boolean mMultiTapInSameArea; private boolean mMovedEnoughForDrag; - private boolean mIsDragCloseToVertical; + private float mInitialDragDirectionXYRatio; public float getLastDownX() { return mLastDownX; @@ -98,8 +98,23 @@ public class EditorTouchState { return mMovedEnoughForDrag; } - public boolean isDragCloseToVertical() { - return mIsDragCloseToVertical && !mIsOnHandle; + /** + * When {@link #isMovedEnoughForDrag()} is {@code true}, this function returns the x/y ratio for + * the initial drag direction. Smaller values indicate that the direction is closer to vertical, + * while larger values indicate that the direction is closer to horizontal. For example: + * <ul> + * <li>if the drag direction is exactly vertical, this returns 0 + * <li>if the drag direction is exactly horizontal, this returns {@link Float#MAX_VALUE} + * <li>if the drag direction is 45 deg from vertical, this returns 1 + * <li>if the drag direction is 30 deg from vertical, this returns 0.58 (x delta is smaller + * than y delta) + * <li>if the drag direction is 60 deg from vertical, this returns 1.73 (x delta is bigger + * than y delta) + * </ul> + * This function never returns negative values, regardless of the direction of the drag. + */ + public float getInitialDragDirectionXYRatio() { + return mInitialDragDirectionXYRatio; } public void setIsOnHandle(boolean onHandle) { @@ -155,7 +170,7 @@ public class EditorTouchState { mLastDownY = event.getY(); mLastDownMillis = event.getEventTime(); mMovedEnoughForDrag = false; - mIsDragCloseToVertical = false; + mInitialDragDirectionXYRatio = 0.0f; } else if (action == MotionEvent.ACTION_UP) { if (TextView.DEBUG_CURSOR) { logCursor("EditorTouchState", "ACTION_UP"); @@ -164,7 +179,7 @@ public class EditorTouchState { mLastUpY = event.getY(); mLastUpMillis = event.getEventTime(); mMovedEnoughForDrag = false; - mIsDragCloseToVertical = false; + mInitialDragDirectionXYRatio = 0.0f; } else if (action == MotionEvent.ACTION_MOVE) { if (!mMovedEnoughForDrag) { float deltaX = event.getX() - mLastDownX; @@ -174,9 +189,8 @@ public class EditorTouchState { int touchSlop = config.getScaledTouchSlop(); mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop; if (mMovedEnoughForDrag) { - // If the direction of the swipe motion is within 45 degrees of vertical, it is - // considered a vertical drag. - mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY); + mInitialDragDirectionXYRatio = (deltaY == 0) ? Float.MAX_VALUE : + Math.abs(deltaX / deltaY); } } } else if (action == MotionEvent.ACTION_CANCEL) { @@ -185,7 +199,7 @@ public class EditorTouchState { mMultiTapStatus = MultiTapStatus.NONE; mMultiTapInSameArea = false; mMovedEnoughForDrag = false; - mIsDragCloseToVertical = false; + mInitialDragDirectionXYRatio = 0.0f; } } @@ -201,4 +215,27 @@ public class EditorTouchState { float distanceSquared = (deltaX * deltaX) + (deltaY * deltaY); return distanceSquared <= maxDistance * maxDistance; } + + /** + * Returns the x/y ratio corresponding to the given angle relative to vertical. Smaller angle + * values (ie, closer to vertical) will result in a smaller x/y ratio. For example: + * <ul> + * <li>if the angle is 45 deg, the ratio is 1 + * <li>if the angle is 30 deg, the ratio is 0.58 (x delta is smaller than y delta) + * <li>if the angle is 60 deg, the ratio is 1.73 (x delta is bigger than y delta) + * </ul> + * If the passed-in value is <= 0, this function returns 0. If the passed-in value is >= 90, + * this function returns {@link Float#MAX_VALUE}. + * + * @see #getInitialDragDirectionXYRatio() + */ + public static float getXYRatio(int angleFromVerticalInDegrees) { + if (angleFromVerticalInDegrees <= 0) { + return 0.0f; + } + if (angleFromVerticalInDegrees >= 90) { + return Float.MAX_VALUE; + } + return (float) Math.tan(Math.toRadians(angleFromVerticalInDegrees)); + } } diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java index 832dd5190d37..1a493653d811 100644 --- a/core/java/android/widget/WidgetFlags.java +++ b/core/java/android/widget/WidgetFlags.java @@ -41,6 +41,28 @@ public final class WidgetFlags { public static final boolean ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT = true; /** + * Threshold for the direction of a swipe gesture in order for it to be handled as a cursor drag + * rather than a scroll. The direction angle of the swipe gesture must exceed this value in + * order to trigger cursor drag; otherwise, the swipe will be assumed to be a scroll gesture. + * The value units for this flag is degrees and the valid range is [0,90] inclusive. If a value + * < 0 is set, 0 will be used instead; if a value > 90 is set, 90 will be used instead. + */ + public static final String CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL = + "CursorControlFeature__min_angle_from_vertical_to_start_cursor_drag"; + + /** + * The key used in app core settings for the flag + * {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}. + */ + public static final String KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL = + "widget__min_angle_from_vertical_to_start_cursor_drag"; + + /** + * Default value for the flag {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}. + */ + public static final int CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT = 45; + + /** * The flag of finger-to-cursor distance in DP for cursor dragging. * The value unit is DP and the range is {0..100}. If the value is out of range, the legacy * value, which is based on handle size, will be used. diff --git a/core/java/android/widget/inline/InlineContentView.java b/core/java/android/widget/inline/InlineContentView.java index 699a7735bbee..9712311aab7c 100644 --- a/core/java/android/widget/inline/InlineContentView.java +++ b/core/java/android/widget/inline/InlineContentView.java @@ -59,7 +59,7 @@ import java.util.function.Consumer; */ public class InlineContentView extends ViewGroup { - private static final String TAG = "InlineContentView_test2"; + private static final String TAG = "InlineContentView"; private static final boolean DEBUG = false; diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 3a89dcd96487..49ad81b2bbc8 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -3134,7 +3134,9 @@ public class ChooserActivity extends ResolverActivity implements // ends up disabled. That's because at some point the old tab's vertical scrolling is // disabled and the new tab's is enabled. For context, see b/159997845 setVerticalScrollEnabled(true); - mResolverDrawerLayout.scrollNestedScrollableChildBackToTop(); + if (mResolverDrawerLayout != null) { + mResolverDrawerLayout.scrollNestedScrollableChildBackToTop(); + } } @Override diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index ea3d2de13ce6..eb59f0f59be1 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -123,10 +123,15 @@ public final class SystemUiDeviceConfigFlags { // Flag related to Privacy Indicators /** - * Whether the Permissions Hub is showing. + * Whether to show the complete ongoing app ops chip. */ public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled"; + /** + * Whether to show app ops chip for just microphone + camera. + */ + public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled"; + // Flags related to Assistant /** diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java index b3ea118d9c73..2620ba0749a9 100644 --- a/core/java/com/android/internal/os/BatterySipper.java +++ b/core/java/com/android/internal/os/BatterySipper.java @@ -129,6 +129,7 @@ public class BatterySipper implements Comparable<BatterySipper> { public double videoPowerMah; public double wakeLockPowerMah; public double wifiPowerMah; + public double systemServiceCpuPowerMah; // **************** // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto) @@ -242,6 +243,7 @@ public class BatterySipper implements Comparable<BatterySipper> { videoPowerMah += other.videoPowerMah; proportionalSmearMah += other.proportionalSmearMah; totalSmearedPowerMah += other.totalSmearedPowerMah; + systemServiceCpuPowerMah += other.systemServiceCpuPowerMah; } /** @@ -253,7 +255,8 @@ public class BatterySipper implements Comparable<BatterySipper> { public double sumPower() { totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah + sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah + - flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah; + flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah + + systemServiceCpuPowerMah; totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah; return totalPowerMah; diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index b131ab83cc79..3dfa3c3f6906 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -133,6 +133,7 @@ public class BatteryStatsHelper { private double mMaxDrainedPower; PowerCalculator mCpuPowerCalculator; + SystemServicePowerCalculator mSystemServicePowerCalculator; PowerCalculator mWakelockPowerCalculator; MobileRadioPowerCalculator mMobileRadioPowerCalculator; PowerCalculator mWifiPowerCalculator; @@ -396,6 +397,11 @@ public class BatteryStatsHelper { } mCpuPowerCalculator.reset(); + if (mSystemServicePowerCalculator == null) { + mSystemServicePowerCalculator = new SystemServicePowerCalculator(mPowerProfile, mStats); + } + mSystemServicePowerCalculator.reset(); + if (mMemoryPowerCalculator == null) { mMemoryPowerCalculator = new MemoryPowerCalculator(mPowerProfile); } @@ -588,6 +594,8 @@ public class BatteryStatsHelper { mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); + mSystemServicePowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, + mStatsType); final double totalPower = app.sumPower(); if (DEBUG && totalPower != 0) { diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 58ba16bc61dd..84981515e133 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -144,6 +144,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final boolean DEBUG = false; public static final boolean DEBUG_ENERGY = false; private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY; + private static final boolean DEBUG_BINDER_STATS = true; private static final boolean DEBUG_MEMORY = false; private static final boolean DEBUG_HISTORY = false; private static final boolean USE_OLD_HISTORY = false; // for debugging. @@ -154,7 +155,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 186 + (USE_OLD_HISTORY ? 1000 : 0); + static final int VERSION = 187 + (USE_OLD_HISTORY ? 1000 : 0); // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -218,10 +219,13 @@ public class BatteryStatsImpl extends BatteryStats { new KernelCpuUidClusterTimeReader(true); @VisibleForTesting protected KernelSingleUidTimeReader mKernelSingleUidTimeReader; + @VisibleForTesting + protected SystemServerCpuThreadReader mSystemServerCpuThreadReader; private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats = new KernelMemoryBandwidthStats(); private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>(); + public LongSparseArray<SamplingTimer> getKernelMemoryStats() { return mKernelMemoryStats; } @@ -267,6 +271,7 @@ public class BatteryStatsImpl extends BatteryStats { /** Container for Rail Energy Data stats. */ private final RailStats mTmpRailStats = new RailStats(); + /** * Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader}, * {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader}, @@ -1007,6 +1012,16 @@ public class BatteryStatsImpl extends BatteryStats { private long[] mCpuFreqs; + /** + * Times spent by the system server threads grouped by cluster and CPU speed. + */ + private LongSamplingCounter[][] mSystemServerThreadCpuTimesUs; + + /** + * Times spent by the system server threads handling incoming binder requests. + */ + private LongSamplingCounter[][] mBinderThreadCpuTimesUs; + @VisibleForTesting protected PowerProfile mPowerProfile; @@ -6131,10 +6146,77 @@ public class BatteryStatsImpl extends BatteryStats { * the power consumption to the calling app. */ public void noteBinderCallStats(int workSourceUid, long incrementalCallCount, - Collection<BinderCallsStats.CallStat> callStats) { + Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) { synchronized (this) { getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(incrementalCallCount, callStats); + mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids); + } + } + + /** + * Estimates the proportion of system server CPU activity handling incoming binder calls + * that can be attributed to each app + */ + @VisibleForTesting + public void updateSystemServiceCallStats() { + // Start off by computing the average duration of recorded binder calls, + // regardless of which binder or transaction. We will use this as a fallback + // for calls that were not sampled at all. + int totalRecordedCallCount = 0; + long totalRecordedCallTimeMicros = 0; + for (int i = 0; i < mUidStats.size(); i++) { + Uid uid = mUidStats.valueAt(i); + ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats; + for (int j = binderCallStats.size() - 1; j >= 0; j--) { + BinderCallStats stats = binderCallStats.valueAt(j); + totalRecordedCallCount += stats.recordedCallCount; + totalRecordedCallTimeMicros += stats.recordedCpuTimeMicros; + } + } + + long totalSystemServiceTimeMicros = 0; + + // For every UID, use recorded durations of sampled binder calls to estimate + // the total time the system server spent handling requests from this UID. + for (int i = 0; i < mUidStats.size(); i++) { + Uid uid = mUidStats.valueAt(i); + + long totalTimeForUid = 0; + int totalCallCountForUid = 0; + ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats; + for (int j = binderCallStats.size() - 1; j >= 0; j--) { + BinderCallStats stats = binderCallStats.valueAt(j); + totalCallCountForUid += stats.callCount; + if (stats.recordedCallCount > 0) { + totalTimeForUid += + stats.callCount * stats.recordedCpuTimeMicros / stats.recordedCallCount; + } else if (totalRecordedCallCount > 0) { + totalTimeForUid += + stats.callCount * totalRecordedCallTimeMicros / totalRecordedCallCount; + } + } + + if (totalCallCountForUid < uid.mBinderCallCount && totalRecordedCallCount > 0) { + // Estimate remaining calls, which were not tracked because of binder call + // stats sampling + totalTimeForUid += + (uid.mBinderCallCount - totalCallCountForUid) * totalRecordedCallTimeMicros + / totalRecordedCallCount; + } + + uid.mSystemServiceTimeUs = totalTimeForUid; + totalSystemServiceTimeMicros += totalTimeForUid; + } + + for (int i = 0; i < mUidStats.size(); i++) { + Uid uid = mUidStats.valueAt(i); + if (totalSystemServiceTimeMicros > 0) { + uid.mProportionalSystemServiceUsage = + (double) uid.mSystemServiceTimeUs / totalSystemServiceTimeMicros; + } else { + uid.mProportionalSystemServiceUsage = 0; + } } } @@ -6583,7 +6665,7 @@ public class BatteryStatsImpl extends BatteryStats { /** * Accumulates stats for a specific binder transaction. */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @VisibleForTesting protected static class BinderCallStats { static final Comparator<BinderCallStats> COMPARATOR = Comparator.comparing(BinderCallStats::getClassName) @@ -6822,6 +6904,16 @@ public class BatteryStatsImpl extends BatteryStats { */ private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>(); + /** + * Estimated total time spent by the system server handling requests from this uid. + */ + private long mSystemServiceTimeUs; + + /** + * Estimated proportion of system server binder call CPU cost for this uid. + */ + private double mProportionalSystemServiceUsage; + public Uid(BatteryStatsImpl bsi, int uid) { mBsi = bsi; mUid = uid; @@ -6899,7 +6991,6 @@ public class BatteryStatsImpl extends BatteryStats { return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED); } - @Override public long[] getCpuFreqTimes(int which, int procState) { if (which < 0 || which >= NUM_PROCESS_STATE) { @@ -6934,10 +7025,16 @@ public class BatteryStatsImpl extends BatteryStats { return mBinderCallCount; } + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public ArraySet<BinderCallStats> getBinderCallStats() { return mBinderCallStats; } + @Override + public double getProportionalSystemServiceUsage() { + return mProportionalSystemServiceUsage; + } + public void addIsolatedUid(int isolatedUid) { if (mChildUids == null) { mChildUids = new IntArray(); @@ -8029,9 +8126,12 @@ public class BatteryStatsImpl extends BatteryStats { mBinderCallCount = 0; mBinderCallStats.clear(); + mProportionalSystemServiceUsage = 0; + mLastStepUserTime = mLastStepSystemTime = 0; mCurStepUserTime = mCurStepSystemTime = 0; + return !active; } @@ -8373,28 +8473,7 @@ public class BatteryStatsImpl extends BatteryStats { mUserCpuTime.writeToParcel(out); mSystemCpuTime.writeToParcel(out); - if (mCpuClusterSpeedTimesUs != null) { - out.writeInt(1); - out.writeInt(mCpuClusterSpeedTimesUs.length); - for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) { - if (cpuSpeeds != null) { - out.writeInt(1); - out.writeInt(cpuSpeeds.length); - for (LongSamplingCounter c : cpuSpeeds) { - if (c != null) { - out.writeInt(1); - c.writeToParcel(out); - } else { - out.writeInt(0); - } - } - } else { - out.writeInt(0); - } - } - } else { - out.writeInt(0); - } + mBsi.writeCpuSpeedCountersToParcel(out, mCpuClusterSpeedTimesUs); LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs); LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs); @@ -8432,6 +8511,7 @@ public class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + out.writeDouble(mProportionalSystemServiceUsage); } void readJobCompletionsFromParcelLocked(Parcel in) { @@ -8692,36 +8772,7 @@ public class BatteryStatsImpl extends BatteryStats { mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); - if (in.readInt() != 0) { - int numCpuClusters = in.readInt(); - if (mBsi.mPowerProfile != null && mBsi.mPowerProfile.getNumCpuClusters() != numCpuClusters) { - throw new ParcelFormatException("Incompatible number of cpu clusters"); - } - - mCpuClusterSpeedTimesUs = new LongSamplingCounter[numCpuClusters][]; - for (int cluster = 0; cluster < numCpuClusters; cluster++) { - if (in.readInt() != 0) { - int numSpeeds = in.readInt(); - if (mBsi.mPowerProfile != null && - mBsi.mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) { - throw new ParcelFormatException("Incompatible number of cpu speeds"); - } - - final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds]; - mCpuClusterSpeedTimesUs[cluster] = cpuSpeeds; - for (int speed = 0; speed < numSpeeds; speed++) { - if (in.readInt() != 0) { - cpuSpeeds[speed] = new LongSamplingCounter( - mBsi.mOnBatteryTimeBase, in); - } - } - } else { - mCpuClusterSpeedTimesUs[cluster] = null; - } - } - } else { - mCpuClusterSpeedTimesUs = null; - } + mCpuClusterSpeedTimesUs = mBsi.readCpuSpeedCountersFromParcel(in); mCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(in, mBsi.mOnBatteryTimeBase); mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel( @@ -8762,6 +8813,8 @@ public class BatteryStatsImpl extends BatteryStats { } else { mWifiRadioApWakeupCount = null; } + + mProportionalSystemServiceUsage = in.readDouble(); } public void noteJobsDeferredLocked(int numDeferred, long sinceLast) { @@ -9904,7 +9957,6 @@ public class BatteryStatsImpl extends BatteryStats { UserInfoProvider userInfoProvider) { init(clocks); - if (systemDir == null) { mStatsFile = null; mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer); @@ -10046,6 +10098,8 @@ public class BatteryStatsImpl extends BatteryStats { firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i); } + mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create(); + if (mEstimatedBatteryCapacity == -1) { // Initialize the estimated battery capacity to a known preset one. mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity(); @@ -10726,6 +10780,9 @@ public class BatteryStatsImpl extends BatteryStats { mTmpRailStats.reset(); + resetIfNotNull(mSystemServerThreadCpuTimesUs, false); + resetIfNotNull(mBinderThreadCpuTimesUs, false); + mLastHistoryStepDetails = null; mLastStepCpuUserTime = mLastStepCpuSystemTime = 0; mCurStepCpuUserTime = mCurStepCpuSystemTime = 0; @@ -10853,7 +10910,7 @@ public class BatteryStatsImpl extends BatteryStats { return null; } - /** + /** * Distribute WiFi energy info and network traffic to apps. * @param info The energy information from the WiFi controller. */ @@ -11772,6 +11829,7 @@ public class BatteryStatsImpl extends BatteryStats { for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) { mKernelCpuSpeedReaders[cluster].readDelta(); } + mSystemServerCpuThreadReader.readDelta(); return; } @@ -11791,6 +11849,87 @@ public class BatteryStatsImpl extends BatteryStats { readKernelUidCpuClusterTimesLocked(onBattery); mNumAllUidCpuTimeReads += 2; } + + updateSystemServerThreadStats(); + } + + /** + * Estimates the proportion of the System Server CPU activity (per cluster per speed) + * spent on handling incoming binder calls. + */ + @VisibleForTesting + public void updateSystemServerThreadStats() { + // There are some simplifying assumptions made in this algorithm + // 1) We assume that if a thread handles incoming binder calls, all of its activity + // is spent doing that. Most incoming calls are handled by threads allocated + // by the native layer in the binder thread pool, so this assumption is reasonable. + // 2) We use the aggregate CPU time spent in different threads as a proxy for the CPU + // cost. In reality, in multi-core CPUs, the CPU cost may not be linearly + // affected by additional threads. + + SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = + mSystemServerCpuThreadReader.readDelta(); + + int index = 0; + int numCpuClusters = mPowerProfile.getNumCpuClusters(); + if (mSystemServerThreadCpuTimesUs == null) { + mSystemServerThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][]; + mBinderThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][]; + } + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + if (mSystemServerThreadCpuTimesUs[cluster] == null) { + mSystemServerThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds]; + mBinderThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds]; + for (int speed = 0; speed < numSpeeds; speed++) { + mSystemServerThreadCpuTimesUs[cluster][speed] = + new LongSamplingCounter(mOnBatteryTimeBase); + mBinderThreadCpuTimesUs[cluster][speed] = + new LongSamplingCounter(mOnBatteryTimeBase); + } + } + for (int speed = 0; speed < numSpeeds; speed++) { + mSystemServerThreadCpuTimesUs[cluster][speed].addCountLocked( + systemServiceCpuThreadTimes.threadCpuTimesUs[index]); + mBinderThreadCpuTimesUs[cluster][speed].addCountLocked( + systemServiceCpuThreadTimes.binderThreadCpuTimesUs[index]); + index++; + } + } + if (DEBUG_BINDER_STATS) { + Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)"); + long binderThreadTime = 0; + long totalThreadTime = 0; + int cpuIndex = 0; + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + StringBuilder sb = new StringBuilder(); + sb.append("cpu").append(cpuIndex).append(": ["); + int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + for (int speed = 0; speed < numSpeeds; speed++) { + if (speed != 0) { + sb.append(", "); + } + long totalCount = mSystemServerThreadCpuTimesUs[cluster][speed].getCountLocked( + 0) / 1000; + long binderCount = mBinderThreadCpuTimesUs[cluster][speed].getCountLocked(0) + / 1000; + sb.append(String.format("%d/%d(%.1f%%)", + binderCount, + totalCount, + totalCount != 0 ? (double) binderCount * 100 / totalCount : 0)); + + totalThreadTime += totalCount; + binderThreadTime += binderCount; + index++; + } + cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster); + Slog.d(TAG, sb.toString()); + } + Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTime); + Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)", + binderThreadTime, + binderThreadTime != 0 ? (double) binderThreadTime * 100 / totalThreadTime : 0)); + } } /** @@ -12998,6 +13137,75 @@ public class BatteryStatsImpl extends BatteryStats { } } + + @Override + public long getSystemServiceTimeAtCpuSpeed(int cluster, int step) { + // Estimates the time spent by the system server handling incoming binder requests. + // + // The data that we can get from the kernel is this: + // - CPU duration for a (thread - cluster - CPU speed) combination + // - CPU duration for a (UID - cluster - CPU speed) combination + // + // The configuration we have in the Power Profile is this: + // - Average CPU power for a (cluster - CPU speed) combination. + // + // The model used by BatteryStats can be illustrated with this example: + // + // - Let's say the system server has 10 threads. + // - These 10 threads spent 1000 ms of CPU time in aggregate + // - Of the 10 threads 4 were execute exclusively incoming binder calls. + // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate + // - The real time spent by the system server UID doing all of this is, say, 200 ms. + // + // We will assume that power consumption is proportional to the time spent by the CPU + // across all threads. This is a crude assumption, but we don't have more detailed data. + // Thus, + // binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime + // + // In our example, + // binderRealTime = 200 * 600 / 1000 = 120ms + // + // We can then multiply this estimated time by the average power to obtain an estimate + // of the total power consumed by incoming binder calls for the given cluster/speed + // combination. + + if (mSystemServerThreadCpuTimesUs == null) { + return 0; + } + + if (cluster < 0 || cluster >= mSystemServerThreadCpuTimesUs.length) { + return 0; + } + + final LongSamplingCounter[] threadTimesForCluster = mSystemServerThreadCpuTimesUs[cluster]; + + if (step < 0 || step >= threadTimesForCluster.length) { + return 0; + } + + Uid systemUid = mUidStats.get(Process.SYSTEM_UID); + if (systemUid == null) { + return 0; + } + + final long uidTimeAtCpuSpeed = systemUid.getTimeAtCpuSpeed(cluster, step, + BatteryStats.STATS_SINCE_CHARGED); + if (uidTimeAtCpuSpeed == 0) { + return 0; + } + + final long uidThreadTime = + threadTimesForCluster[step].getCountLocked(BatteryStats.STATS_SINCE_CHARGED); + + if (uidThreadTime == 0) { + return 0; + } + + final long binderThreadTime = mBinderThreadCpuTimesUs[cluster][step].getCountLocked( + BatteryStats.STATS_SINCE_CHARGED); + return uidTimeAtCpuSpeed * binderThreadTime / uidThreadTime; + } + /** * Retrieve the statistics object for a particular uid, creating if needed. */ @@ -13327,45 +13535,7 @@ public class BatteryStatsImpl extends BatteryStats { pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" "); pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000); } - pw.println("Per UID system service calls:"); - BinderTransactionNameResolver nameResolver = new BinderTransactionNameResolver(); - for (int i = 0; i < size; i++) { - int u = mUidStats.keyAt(i); - Uid uid = mUidStats.get(u); - long binderCallCount = uid.getBinderCallCount(); - if (binderCallCount != 0) { - pw.print(" "); - pw.print(u); - pw.print(" system service calls: "); - pw.print(binderCallCount); - ArraySet<BinderCallStats> binderCallStats = uid.getBinderCallStats(); - if (!binderCallStats.isEmpty()) { - pw.println(", including"); - BinderCallStats[] bcss = new BinderCallStats[binderCallStats.size()]; - binderCallStats.toArray(bcss); - for (BinderCallStats bcs : bcss) { - bcs.ensureMethodName(nameResolver); - } - Arrays.sort(bcss, BinderCallStats.COMPARATOR); - for (BinderCallStats callStats : bcss) { - pw.print(" "); - pw.print(callStats.getClassName()); - pw.print('#'); - pw.print(callStats.getMethodName()); - pw.print(" calls: "); - pw.print(callStats.callCount); - if (callStats.recordedCallCount != 0) { - pw.print(" time: "); - pw.print(callStats.callCount * callStats.recordedCpuTimeMicros - / callStats.recordedCallCount / 1000); - } - pw.println(); - } - } else { - pw.println(); - } - } - } + pw.println("Per UID CPU active time in ms:"); for (int i = 0; i < size; i++) { int u = mUidStats.keyAt(i); @@ -13390,6 +13560,30 @@ public class BatteryStatsImpl extends BatteryStats { pw.print(" "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times)); } } + + updateSystemServiceCallStats(); + if (mSystemServerThreadCpuTimesUs != null) { + pw.println("Per UID System server binder time in ms:"); + for (int i = 0; i < size; i++) { + int u = mUidStats.keyAt(i); + Uid uid = mUidStats.get(u); + double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage(); + + long time = 0; + for (int cluster = 0; cluster < mSystemServerThreadCpuTimesUs.length; cluster++) { + int numSpeeds = mSystemServerThreadCpuTimesUs[cluster].length; + for (int speed = 0; speed < numSpeeds; speed++) { + time += getSystemServiceTimeAtCpuSpeed(cluster, speed) + * proportionalSystemServiceUsage; + } + } + + pw.print(" "); + pw.print(u); + pw.print(": "); + pw.println(time); + } + } } final ReentrantLock mWriteLock = new ReentrantLock(); @@ -14908,6 +15102,9 @@ public class BatteryStatsImpl extends BatteryStats { u.readFromParcelLocked(mOnBatteryTimeBase, mOnBatteryScreenOffTimeBase, in); mUidStats.append(uid, u); } + + mSystemServerThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in); + mBinderThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in); } public void writeToParcel(Parcel out, int flags) { @@ -14923,6 +15120,8 @@ public class BatteryStatsImpl extends BatteryStats { // Need to update with current kernel wake lock counts. pullPendingStateUpdatesLocked(); + updateSystemServiceCallStats(); + // Pull the clock time. This may update the time and make a new history entry // if we had originally pulled a time before the RTC was set. getStartClockTime(); @@ -15105,6 +15304,73 @@ public class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + writeCpuSpeedCountersToParcel(out, mSystemServerThreadCpuTimesUs); + writeCpuSpeedCountersToParcel(out, mBinderThreadCpuTimesUs); + } + + private void writeCpuSpeedCountersToParcel(Parcel out, LongSamplingCounter[][] counters) { + if (counters == null) { + out.writeInt(0); + return; + } + + out.writeInt(1); + out.writeInt(counters.length); + for (int i = 0; i < counters.length; i++) { + LongSamplingCounter[] counterArray = counters[i]; + if (counterArray == null) { + out.writeInt(0); + continue; + } + + out.writeInt(1); + out.writeInt(counterArray.length); + for (int j = 0; j < counterArray.length; j++) { + LongSamplingCounter c = counterArray[j]; + if (c != null) { + out.writeInt(1); + c.writeToParcel(out); + } else { + out.writeInt(0); + } + } + } + } + + private LongSamplingCounter[][] readCpuSpeedCountersFromParcel(Parcel in) { + LongSamplingCounter[][] counters; + if (in.readInt() != 0) { + int numCpuClusters = in.readInt(); + if (mPowerProfile != null + && mPowerProfile.getNumCpuClusters() != numCpuClusters) { + throw new ParcelFormatException("Incompatible number of cpu clusters"); + } + + counters = new LongSamplingCounter[numCpuClusters][]; + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + if (in.readInt() != 0) { + int numSpeeds = in.readInt(); + if (mPowerProfile != null + && mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) { + throw new ParcelFormatException("Incompatible number of cpu speeds"); + } + + final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds]; + counters[cluster] = cpuSpeeds; + for (int speed = 0; speed < numSpeeds; speed++) { + if (in.readInt() != 0) { + cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in); + } + } + } else { + counters[cluster] = null; + } + } + } else { + counters = null; + } + + return counters; } @UnsupportedAppUsage diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index e09ef49acd10..201626abd820 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -115,7 +115,8 @@ public class BinderCallsStats implements BinderInternal.Observer { if (uidEntry != null) { ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats; mCallStatsObserver.noteCallStats(uidEntry.workSourceUid, - uidEntry.incrementalCallCount, callStats.values()); + uidEntry.incrementalCallCount, callStats.values(), + mNativeTids.toArray()); uidEntry.incrementalCallCount = 0; for (int j = callStats.size() - 1; j >= 0; j--) { callStats.valueAt(j).incrementalCallCount = 0; diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java index feb5aab94adc..f14d5f2bbbeb 100644 --- a/core/java/com/android/internal/os/BinderInternal.java +++ b/core/java/com/android/internal/os/BinderInternal.java @@ -142,7 +142,8 @@ public class BinderInternal { * Notes incoming binder call stats associated with this work source UID. */ void noteCallStats(int workSourceUid, long incrementalCallCount, - Collection<BinderCallsStats.CallStat> callStats); + Collection<BinderCallsStats.CallStat> callStats, + int[] binderThreadNativeTids); } /** diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java index 34076700cd95..2ba372a47cf3 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java @@ -225,19 +225,22 @@ public class KernelCpuThreadReader { /** Set the number of frequency buckets to use */ void setNumBuckets(int numBuckets) { - if (numBuckets < 1) { - Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets); - return; - } // If `numBuckets` hasn't changed since the last set, do nothing if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) { return; } - mFrequencyBucketCreator = - new FrequencyBucketCreator(mProcTimeInStateReader.getFrequenciesKhz(), numBuckets); - mFrequenciesKhz = - mFrequencyBucketCreator.bucketFrequencies( - mProcTimeInStateReader.getFrequenciesKhz()); + + final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz(); + if (numBuckets != 0) { + mFrequencyBucketCreator = new FrequencyBucketCreator(frequenciesKhz, numBuckets); + mFrequenciesKhz = mFrequencyBucketCreator.bucketFrequencies(frequenciesKhz); + } else { + mFrequencyBucketCreator = null; + mFrequenciesKhz = new int[frequenciesKhz.length]; + for (int i = 0; i < frequenciesKhz.length; i++) { + mFrequenciesKhz[i] = (int) frequenciesKhz[i]; + } + } } /** Set the UID predicate for {@link #getProcessCpuUsage} */ @@ -320,8 +323,15 @@ public class KernelCpuThreadReader { if (cpuUsagesLong == null) { return null; } - int[] cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong); - + final int[] cpuUsages; + if (mFrequencyBucketCreator != null) { + cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong); + } else { + cpuUsages = new int[cpuUsagesLong.length]; + for (int i = 0; i < cpuUsagesLong.length; i++) { + cpuUsages[i] = (int) cpuUsagesLong[i]; + } + } return new ThreadCpuUsage(threadId, threadName, cpuUsages); } diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java new file mode 100644 index 000000000000..1cdd42c7403e --- /dev/null +++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2020 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.os; + +import android.os.Process; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage + * by various threads of the System Server. + */ +public class SystemServerCpuThreadReader { + private KernelCpuThreadReader mKernelCpuThreadReader; + private int[] mBinderThreadNativeTids; + + private int[] mThreadCpuTimesUs; + private int[] mBinderThreadCpuTimesUs; + private long[] mLastThreadCpuTimesUs; + private long[] mLastBinderThreadCpuTimesUs; + + /** + * Times (in microseconds) spent by the system server UID. + */ + public static class SystemServiceCpuThreadTimes { + // All threads + public long[] threadCpuTimesUs; + // Just the threads handling incoming binder calls + public long[] binderThreadCpuTimesUs; + } + + private SystemServiceCpuThreadTimes mDeltaCpuThreadTimes = new SystemServiceCpuThreadTimes(); + + /** + * Creates a configured instance of SystemServerCpuThreadReader. + */ + public static SystemServerCpuThreadReader create() { + return new SystemServerCpuThreadReader( + KernelCpuThreadReader.create(0, uid -> uid == Process.myUid())); + } + + @VisibleForTesting + public SystemServerCpuThreadReader(Path procPath, int systemServerUid) throws IOException { + this(new KernelCpuThreadReader(0, uid -> uid == systemServerUid, null, null, + new KernelCpuThreadReader.Injector() { + @Override + public int getUidForPid(int pid) { + return systemServerUid; + } + })); + } + + @VisibleForTesting + public SystemServerCpuThreadReader(KernelCpuThreadReader kernelCpuThreadReader) { + mKernelCpuThreadReader = kernelCpuThreadReader; + } + + public void setBinderThreadNativeTids(int[] nativeTids) { + mBinderThreadNativeTids = nativeTids; + } + + /** + * Returns delta of CPU times, per thread, since the previous call to this method. + */ + public SystemServiceCpuThreadTimes readDelta() { + if (mBinderThreadCpuTimesUs == null) { + int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz().length; + mThreadCpuTimesUs = new int[numCpuFrequencies]; + mBinderThreadCpuTimesUs = new int[numCpuFrequencies]; + + mLastThreadCpuTimesUs = new long[numCpuFrequencies]; + mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies]; + + mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies]; + mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies]; + } + + Arrays.fill(mThreadCpuTimesUs, 0); + Arrays.fill(mBinderThreadCpuTimesUs, 0); + + ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsage = + mKernelCpuThreadReader.getProcessCpuUsage(); + int processCpuUsageSize = processCpuUsage.size(); + for (int i = 0; i < processCpuUsageSize; i++) { + KernelCpuThreadReader.ProcessCpuUsage pcu = processCpuUsage.get(i); + ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = pcu.threadCpuUsages; + if (threadCpuUsages != null) { + int threadCpuUsagesSize = threadCpuUsages.size(); + for (int j = 0; j < threadCpuUsagesSize; j++) { + KernelCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(j); + boolean isBinderThread = isBinderThread(tcu.threadId); + + final int len = Math.min(tcu.usageTimesMillis.length, mThreadCpuTimesUs.length); + for (int k = 0; k < len; k++) { + int usageTimeUs = tcu.usageTimesMillis[k] * 1000; + mThreadCpuTimesUs[k] += usageTimeUs; + if (isBinderThread) { + mBinderThreadCpuTimesUs[k] += usageTimeUs; + } + } + } + } + } + + for (int i = 0; i < mThreadCpuTimesUs.length; i++) { + if (mThreadCpuTimesUs[i] < mLastThreadCpuTimesUs[i]) { + mDeltaCpuThreadTimes.threadCpuTimesUs[i] = mThreadCpuTimesUs[i]; + mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i]; + } else { + mDeltaCpuThreadTimes.threadCpuTimesUs[i] = + mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i]; + mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = + mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i]; + } + mLastThreadCpuTimesUs[i] = mThreadCpuTimesUs[i]; + mLastBinderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i]; + } + + return mDeltaCpuThreadTimes; + } + + private boolean isBinderThread(int threadId) { + if (mBinderThreadNativeTids != null) { + for (int i = 0; i < mBinderThreadNativeTids.length; i++) { + if (threadId == mBinderThreadNativeTids[i]) { + return true; + } + } + } + return false; + } +} diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java new file mode 100644 index 000000000000..481b901b3c69 --- /dev/null +++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 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.os; + +import android.os.BatteryStats; +import android.util.Log; + +import java.util.Arrays; + +/** + * Estimates the amount of power consumed by the System Server handling requests from + * a given app. + */ +public class SystemServicePowerCalculator extends PowerCalculator { + private static final boolean DEBUG = false; + private static final String TAG = "SystemServicePowerCalc"; + + private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000; + + private final PowerProfile mPowerProfile; + private final BatteryStats mBatteryStats; + // Tracks system server CPU [cluster][speed] power in milliAmp-microseconds + private double[][] mSystemServicePowerMaUs; + + public SystemServicePowerCalculator(PowerProfile powerProfile, BatteryStats batteryStats) { + mPowerProfile = powerProfile; + mBatteryStats = batteryStats; + } + + @Override + public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + long rawUptimeUs, int statsType) { + final double proportionalUsage = u.getProportionalSystemServiceUsage(); + if (proportionalUsage > 0) { + if (mSystemServicePowerMaUs == null) { + updateSystemServicePower(); + } + + double cpuPowerMaUs = 0; + int numCpuClusters = mPowerProfile.getNumCpuClusters(); + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + for (int speed = 0; speed < numSpeeds; speed++) { + cpuPowerMaUs += mSystemServicePowerMaUs[cluster][speed] * proportionalUsage; + } + } + + app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR; + } + } + + private void updateSystemServicePower() { + final int numCpuClusters = mPowerProfile.getNumCpuClusters(); + mSystemServicePowerMaUs = new double[numCpuClusters][]; + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + mSystemServicePowerMaUs[cluster] = new double[numSpeeds]; + for (int speed = 0; speed < numSpeeds; speed++) { + mSystemServicePowerMaUs[cluster][speed] = + mBatteryStats.getSystemServiceTimeAtCpuSpeed(cluster, speed) + * mPowerProfile.getAveragePowerForCpuCore(cluster, speed); + } + } + if (DEBUG) { + Log.d(TAG, "System service power per CPU cluster and frequency"); + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + Log.d(TAG, "Cluster[" + cluster + "]: " + + Arrays.toString(mSystemServicePowerMaUs[cluster])); + } + } + } + + @Override + public void reset() { + mSystemServicePowerMaUs = null; + } +} diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java index ba60fa590792..b42ea7d0b769 100644 --- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java +++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java @@ -16,14 +16,8 @@ package com.android.internal.os.logging; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.PackageManager.NameNotFoundException; -import android.util.Pair; import android.view.WindowManager.LayoutParams; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FrameworkStatsLog; /** @@ -32,81 +26,6 @@ import com.android.internal.util.FrameworkStatsLog; */ public class MetricsLoggerWrapper { - private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0; - private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1; - - public static void logPictureInPictureDismissByTap(Context context, - Pair<ComponentName, Integer> topActivityInfo) { - MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED, - METRIC_VALUE_DISMISSED_BY_TAP); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - getUid(context, topActivityInfo.first, topActivityInfo.second), - topActivityInfo.first.flattenToString(), - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED); - } - - public static void logPictureInPictureDismissByDrag(Context context, - Pair<ComponentName, Integer> topActivityInfo) { - MetricsLogger.action(context, - MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED, - METRIC_VALUE_DISMISSED_BY_DRAG); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - getUid(context, topActivityInfo.first, topActivityInfo.second), - topActivityInfo.first.flattenToString(), - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED); - } - - public static void logPictureInPictureMinimize(Context context, boolean isMinimized, - Pair<ComponentName, Integer> topActivityInfo) { - MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED, - isMinimized); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - getUid(context, topActivityInfo.first, topActivityInfo.second), - topActivityInfo.first.flattenToString(), - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED); - } - - /** - * Get uid from component name and user Id - * @return uid. -1 if not found. - */ - private static int getUid(Context context, ComponentName componentName, int userId) { - int uid = -1; - if (componentName == null) { - return uid; - } - try { - uid = context.getPackageManager().getApplicationInfoAsUser( - componentName.getPackageName(), 0, userId).uid; - } catch (NameNotFoundException e) { - } - return uid; - } - - public static void logPictureInPictureMenuVisible(Context context, boolean menuStateFull) { - MetricsLogger.visibility(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU, - menuStateFull); - } - - public static void logPictureInPictureEnter(Context context, - int uid, String shortComponentName, boolean supportsEnterPipOnTaskSwitch) { - MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED, - supportsEnterPipOnTaskSwitch); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, uid, - shortComponentName, - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED); - } - - public static void logPictureInPictureFullScreen(Context context, int uid, - String shortComponentName) { - MetricsLogger.action(context, - MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - uid, - shortComponentName, - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN); - } - public static void logAppOverlayEnter(int uid, String packageName, boolean changed, int type, boolean usingAlertWindow) { if (changed) { if (type != LayoutParams.TYPE_APPLICATION_OVERLAY) { diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp index ff73c74e125e..7756a62df655 100644 --- a/core/jni/android_hardware_input_InputApplicationHandle.cpp +++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp @@ -30,7 +30,7 @@ namespace android { static struct { jfieldID ptr; jfieldID name; - jfieldID dispatchingTimeoutNanos; + jfieldID dispatchingTimeoutMillis; jfieldID token; } gInputApplicationHandleClassInfo; @@ -61,8 +61,8 @@ bool NativeInputApplicationHandle::updateInfo() { mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "<null>"); - mInfo.dispatchingTimeoutNanos = - env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutNanos); + mInfo.dispatchingTimeoutMillis = + env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutMillis); jobject tokenObj = env->GetObjectField(obj, gInputApplicationHandleClassInfo.token); @@ -144,9 +144,8 @@ int register_android_view_InputApplicationHandle(JNIEnv* env) { GET_FIELD_ID(gInputApplicationHandleClassInfo.name, clazz, "name", "Ljava/lang/String;"); - GET_FIELD_ID(gInputApplicationHandleClassInfo.dispatchingTimeoutNanos, - clazz, - "dispatchingTimeoutNanos", "J"); + GET_FIELD_ID(gInputApplicationHandleClassInfo.dispatchingTimeoutMillis, clazz, + "dispatchingTimeoutMillis", "J"); GET_FIELD_ID(gInputApplicationHandleClassInfo.token, clazz, "token", "Landroid/os/IBinder;"); diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index 796c5c4cc521..ecdba3fcb023 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -47,7 +47,7 @@ static struct { jfieldID name; jfieldID layoutParamsFlags; jfieldID layoutParamsType; - jfieldID dispatchingTimeoutNanos; + jfieldID dispatchingTimeoutMillis; jfieldID frameLeft; jfieldID frameTop; jfieldID frameRight; @@ -118,8 +118,8 @@ bool NativeInputWindowHandle::updateInfo() { env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags)); mInfo.type = static_cast<InputWindowInfo::Type>( env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType)); - mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)( - env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutNanos)); + mInfo.dispatchingTimeout = std::chrono::milliseconds( + env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis)); mInfo.frameLeft = env->GetIntField(obj, gInputWindowHandleClassInfo.frameLeft); mInfo.frameTop = env->GetIntField(obj, @@ -293,8 +293,8 @@ int register_android_view_InputWindowHandle(JNIEnv* env) { GET_FIELD_ID(gInputWindowHandleClassInfo.layoutParamsType, clazz, "layoutParamsType", "I"); - GET_FIELD_ID(gInputWindowHandleClassInfo.dispatchingTimeoutNanos, clazz, - "dispatchingTimeoutNanos", "J"); + GET_FIELD_ID(gInputWindowHandleClassInfo.dispatchingTimeoutMillis, clazz, + "dispatchingTimeoutMillis", "J"); GET_FIELD_ID(gInputWindowHandleClassInfo.frameLeft, clazz, "frameLeft", "I"); diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index 7daefd3e6544..e715be21f146 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -95,8 +95,8 @@ void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDispla ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); if (receiverObj.get()) { ALOGV("receiver %p ~ Invoking vsync handler.", this); - env->CallVoidMethod(receiverObj.get(), - gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count); + env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync, + timestamp, displayId.value, count); ALOGV("receiver %p ~ Returned from vsync handler.", this); } @@ -110,8 +110,8 @@ void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, PhysicalDisp ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); if (receiverObj.get()) { ALOGV("receiver %p ~ Invoking hotplug handler.", this); - env->CallVoidMethod(receiverObj.get(), - gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, displayId, connected); + env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchHotplug, + timestamp, displayId.value, connected); ALOGV("receiver %p ~ Returned from hotplug handler.", this); } @@ -126,9 +126,8 @@ void NativeDisplayEventReceiver::dispatchConfigChanged( jniGetReferent(env, mReceiverWeakGlobal)); if (receiverObj.get()) { ALOGV("receiver %p ~ Invoking config changed handler.", this); - env->CallVoidMethod(receiverObj.get(), - gDisplayEventReceiverClassInfo.dispatchConfigChanged, - timestamp, displayId, configId); + env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchConfigChanged, + timestamp, displayId.value, configId); ALOGV("receiver %p ~ Returned from config changed handler.", this); } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 814a07e7f2df..d6a773fb91e0 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -703,7 +703,7 @@ static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) { jlong* values = env->GetLongArrayElements(array, 0); for (size_t i = 0; i < displayIds.size(); ++i) { - values[i] = static_cast<jlong>(displayIds[i]); + values[i] = static_cast<jlong>(displayIds[i].value); } env->ReleaseLongArrayElements(array, values, 0); @@ -711,7 +711,8 @@ static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) { } static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) { - sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(physicalDisplayId); + sp<IBinder> token = + SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId)); return javaObjectForIBinder(env, token); } diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java index df2946c97d20..c37a34a68549 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java @@ -201,6 +201,38 @@ public class EditorCursorDragTest { } @Test + public void testCursorDrag_diagonal_thresholdConfig() throws Throwable { + TextView tv = mActivity.findViewById(R.id.textview); + Editor editor = tv.getEditorForTesting(); + + StringBuilder sb = new StringBuilder(); + for (int i = 1; i <= 9; i++) { + sb.append("here is some text").append(i).append("\n"); + } + sb.append(Strings.repeat("abcdefghij\n", 400)).append("Last"); + String text = sb.toString(); + onView(withId(R.id.textview)).perform(replaceText(text)); + + int index = text.indexOf("text9"); + onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index)); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index)); + + // Configure the drag direction threshold to require the drag to be exactly horizontal. With + // this set, a swipe that is slightly off horizontal should not trigger cursor drag. + editor.setCursorDragMinAngleFromVertical(90); + int startIdx = text.indexOf("5"); + int endIdx = text.indexOf("here is some text3"); + onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx)); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index)); + + // Configure the drag direction threshold to require the drag to be 45 degrees or more from + // vertical. With this set, the same swipe gesture as above should now trigger cursor drag. + editor.setCursorDragMinAngleFromVertical(45); + onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx)); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(endIdx)); + } + + @Test public void testCursorDrag_vertical_whenTextViewContentsFitOnScreen() throws Throwable { String text = "012345_aaa\n" + "0123456789\n" diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java index 35fd4bd7dc14..94f43def240d 100644 --- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java +++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java @@ -165,7 +165,7 @@ public class EditorTouchStateTest { long event2Time = 1001; MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 20f, 30f, 0, 0, false); + assertDrag(mTouchState, 20f, 30f, 0, 0, 180f); // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout. long event3Time = 5000; @@ -280,7 +280,7 @@ public class EditorTouchStateTest { long event3Time = 1002; MotionEvent event3 = moveEvent(event3Time, event3Time, newX, newY); mTouchState.update(event3, mConfig); - assertDrag(mTouchState, 20f, 30f, 0, 0, false); + assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE); // Simulate an ACTION_UP event. long event4Time = 1003; @@ -301,15 +301,15 @@ public class EditorTouchStateTest { long event2Time = 1002; MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 174f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, true); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f); // Simulate another ACTION_MOVE event that is horizontal from the original down event. - // The value of `isDragCloseToVertical` should NOT change since it should only reflect the - // initial direction of movement. + // The drag direction ratio should NOT change since it should only reflect the initial + // direction of movement. long event3Time = 1003; MotionEvent event3 = moveEvent(event1Time, event3Time, 200f, 0f); mTouchState.update(event3, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, true); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f); // Simulate an ACTION_UP event. long event4Time = 1004; @@ -330,15 +330,15 @@ public class EditorTouchStateTest { long event2Time = 1002; MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, false); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f); // Simulate another ACTION_MOVE event that is vertical from the original down event. - // The value of `isDragCloseToVertical` should NOT change since it should only reflect the - // initial direction of movement. + // The drag direction ratio should NOT change since it should only reflect the initial + // direction of movement. long event3Time = 1003; MotionEvent event3 = moveEvent(event1Time, event3Time, 0f, 200f); mTouchState.update(event3, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, false); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f); // Simulate an ACTION_UP event. long event4Time = 1004; @@ -374,7 +374,7 @@ public class EditorTouchStateTest { long event2Time = 1002; MotionEvent event2 = moveEvent(event2Time, event2Time, 200f, 30f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 20f, 30f, 0, 0, false); + assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE); // Simulate an ACTION_CANCEL event. long event3Time = 1003; @@ -411,6 +411,84 @@ public class EditorTouchStateTest { assertSingleTap(mTouchState, 22f, 33f, 20f, 30f); } + @Test + public void testGetXYRatio() throws Exception { + doTestGetXYRatio(-1, 0.0f); + doTestGetXYRatio(0, 0.0f); + doTestGetXYRatio(30, 0.58f); + doTestGetXYRatio(45, 1.0f); + doTestGetXYRatio(60, 1.73f); + doTestGetXYRatio(90, Float.MAX_VALUE); + doTestGetXYRatio(91, Float.MAX_VALUE); + } + + private void doTestGetXYRatio(int angleFromVerticalInDegrees, float expectedXYRatioRounded) { + float result = EditorTouchState.getXYRatio(angleFromVerticalInDegrees); + String msg = String.format( + "%d deg should give an x/y ratio of %f; actual unrounded result is %f", + angleFromVerticalInDegrees, expectedXYRatioRounded, result); + float roundedResult = (result == 0.0f || result == Float.MAX_VALUE) ? result : + Math.round(result * 100) / 100f; + assertThat(msg, roundedResult, is(expectedXYRatioRounded)); + } + + @Test + public void testUpdate_dragDirection() throws Exception { + // Simulate moving straight up. + doTestDragDirection(100f, 100f, 100f, 50f, 0f); + + // Simulate moving straight down. + doTestDragDirection(100f, 100f, 100f, 150f, 0f); + + // Simulate moving straight left. + doTestDragDirection(100f, 100f, 50f, 100f, Float.MAX_VALUE); + + // Simulate moving straight right. + doTestDragDirection(100f, 100f, 150f, 100f, Float.MAX_VALUE); + + // Simulate moving up and right, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 110f, 50f, 10f / 50f); + + // Simulate moving up and right, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 150f, 90f, 50f / 10f); + + // Simulate moving down and right, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 110f, 150f, 10f / 50f); + + // Simulate moving down and right, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 150f, 110f, 50f / 10f); + + // Simulate moving down and left, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 90f, 150f, 10f / 50f); + + // Simulate moving down and left, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 50f, 110f, 50f / 10f); + + // Simulate moving up and left, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 90f, 50f, 10f / 50f); + + // Simulate moving up and left, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 50f, 90f, 50f / 10f); + } + + private void doTestDragDirection(float downX, float downY, float moveX, float moveY, + float expectedInitialDragDirectionXYRatio) { + EditorTouchState touchState = new EditorTouchState(); + + // Simulate an ACTION_DOWN event. + long event1Time = 1001; + MotionEvent event1 = downEvent(event1Time, event1Time, downX, downY); + touchState.update(event1, mConfig); + + // Simulate an ACTION_MOVE event. + long event2Time = 1002; + MotionEvent event2 = moveEvent(event1Time, event2Time, moveX, moveY); + touchState.update(event2, mConfig); + String msg = String.format("(%.0f,%.0f)=>(%.0f,%.0f)", downX, downY, moveX, moveY); + assertThat(msg, touchState.getInitialDragDirectionXYRatio(), + is(expectedInitialDragDirectionXYRatio)); + } + private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) { return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0); } @@ -441,7 +519,7 @@ public class EditorTouchStateTest { } private static void assertDrag(EditorTouchState touchState, float lastDownX, - float lastDownY, float lastUpX, float lastUpY, boolean isDragCloseToVertical) { + float lastDownY, float lastUpX, float lastUpY, float initialDragDirectionXYRatio) { assertThat(touchState.getLastDownX(), is(lastDownX)); assertThat(touchState.getLastDownY(), is(lastDownY)); assertThat(touchState.getLastUpX(), is(lastUpX)); @@ -451,7 +529,7 @@ public class EditorTouchStateTest { assertThat(touchState.isMultiTap(), is(false)); assertThat(touchState.isMultiTapInSameArea(), is(false)); assertThat(touchState.isMovedEnoughForDrag(), is(true)); - assertThat(touchState.isDragCloseToVertical(), is(isDragCloseToVertical)); + assertThat(touchState.getInitialDragDirectionXYRatio(), is(initialDragDirectionXYRatio)); } private static void assertMultiTap(EditorTouchState touchState, @@ -467,6 +545,5 @@ public class EditorTouchStateTest { || multiTapStatus == MultiTapStatus.TRIPLE_CLICK)); assertThat(touchState.isMultiTapInSameArea(), is(isMultiTapInSameArea)); assertThat(touchState.isMovedEnoughForDrag(), is(false)); - assertThat(touchState.isDragCloseToVertical(), is(false)); } } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java index 3e67b8bffa63..22c41f3c9622 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java @@ -38,7 +38,8 @@ import java.util.Collection; @SmallTest public class BatteryStatsBinderCallStatsTest extends TestCase { - private static final int TRANSACTION_CODE = 100; + private static final int TRANSACTION_CODE1 = 100; + private static final int TRANSACTION_CODE2 = 101; /** * Test BatteryStatsImpl.Uid.noteBinderCallStats. @@ -53,23 +54,23 @@ public class BatteryStatsBinderCallStatsTest extends TestCase { Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(callingUid, - MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */); + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); stat1.incrementalCallCount = 21; stat1.recordedCallCount = 5; stat1.cpuTimeMicros = 1000; callStats.add(stat1); - bi.noteBinderCallStats(workSourceUid, 42, callStats); + bi.noteBinderCallStats(workSourceUid, 42, callStats, null); callStats.clear(); BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid, - MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */); + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); stat2.incrementalCallCount = 9; stat2.recordedCallCount = 8; stat2.cpuTimeMicros = 500; callStats.add(stat2); - bi.noteBinderCallStats(workSourceUid, 8, callStats); + bi.noteBinderCallStats(workSourceUid, 8, callStats, null); BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid); assertEquals(42 + 8, uid.getBinderCallCount()); @@ -85,9 +86,62 @@ public class BatteryStatsBinderCallStatsTest extends TestCase { assertEquals(500, value.recordedCpuTimeMicros); } + + @Test + public void testProportionalSystemServiceUsage_noStatsForSomeMethods() throws Exception { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + + int callingUid = Process.FIRST_APPLICATION_UID + 1; + int workSourceUid1 = Process.FIRST_APPLICATION_UID + 1; + int workSourceUid2 = Process.FIRST_APPLICATION_UID + 2; + int workSourceUid3 = Process.FIRST_APPLICATION_UID + 3; + + Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); + BinderCallsStats.CallStat stat1a = new BinderCallsStats.CallStat(callingUid, + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); + stat1a.incrementalCallCount = 10; + stat1a.recordedCallCount = 5; + stat1a.cpuTimeMicros = 1000; + callStats.add(stat1a); + + BinderCallsStats.CallStat stat1b = new BinderCallsStats.CallStat(callingUid, + MockBinder.class, TRANSACTION_CODE2, true /*screenInteractive */); + stat1b.incrementalCallCount = 30; + stat1b.recordedCallCount = 15; + stat1b.cpuTimeMicros = 1500; + callStats.add(stat1b); + + bi.noteBinderCallStats(workSourceUid1, 65, callStats, null); + + // No recorded stats for some methods. Must use the global average. + callStats.clear(); + BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid, + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); + stat2.incrementalCallCount = 10; + callStats.add(stat2); + + bi.noteBinderCallStats(workSourceUid2, 40, callStats, null); + + // No stats for any calls. Must use the global average + callStats.clear(); + bi.noteBinderCallStats(workSourceUid3, 50, callStats, null); + + bi.updateSystemServiceCallStats(); + + double prop1 = bi.getUidStatsLocked(workSourceUid1).getProportionalSystemServiceUsage(); + double prop2 = bi.getUidStatsLocked(workSourceUid2).getProportionalSystemServiceUsage(); + double prop3 = bi.getUidStatsLocked(workSourceUid3).getProportionalSystemServiceUsage(); + + assertEquals(0.419, prop1, 0.01); + assertEquals(0.258, prop2, 0.01); + assertEquals(0.323, prop3, 0.01); + assertEquals(1.000, prop1 + prop2 + prop3, 0.01); + } + private static class MockBinder extends Binder { public static String getDefaultTransactionName(int txCode) { - return txCode == TRANSACTION_CODE ? "testMethod" : "unknown"; + return txCode == TRANSACTION_CODE1 ? "testMethod" : "unknown"; } } } diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index a5117a3e7cc3..188ba9e0ca0e 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -768,8 +768,8 @@ public class BinderCallsStatsTest { final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>(); bcs.setCallStatsObserver( - (workSourceUid, incrementalCallCount, callStats) -> callStatsList.addAll( - callStats)); + (workSourceUid, incrementalCallCount, callStats, binderThreadIds) -> + callStatsList.addAll(callStats)); Binder binder = new Binder(); diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index bc0e0a496d80..75dd7fb82f30 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -133,6 +133,12 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { return this; } + public MockBatteryStatsImpl setSystemServerCpuThreadReader( + SystemServerCpuThreadReader systemServerCpuThreadReader) { + mSystemServerCpuThreadReader = systemServerCpuThreadReader; + return this; + } + public MockBatteryStatsImpl setUserInfoProvider(UserInfoProvider provider) { mUserInfoProvider = provider; return this; diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java new file mode 100644 index 000000000000..10ba54865dbe --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 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.os; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.os.FileUtils; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SystemServerCpuThreadReaderTest { + private File mProcDirectory; + + @Before + public void setUp() { + Context context = InstrumentationRegistry.getContext(); + mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE); + } + + @After + public void tearDown() throws Exception { + FileUtils.deleteContents(mProcDirectory); + } + + @Test + public void testReaderDelta_firstTime() throws IOException { + int uid = 42; + setupDirectory( + mProcDirectory.toPath().resolve(String.valueOf(uid)), + new int[]{42, 1, 2, 3}, + new int[]{1000, 2000}, + // Units are 10ms aka 10000Us + new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}}); + + SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader( + mProcDirectory.toPath(), uid); + reader.setBinderThreadNativeTids(new int[]{1, 3}); + SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = + reader.readDelta(); + assertArrayEquals(new long[]{100 * 10000, 1100 * 10000}, + systemServiceCpuThreadTimes.threadCpuTimesUs); + assertArrayEquals(new long[]{0, 600 * 10000}, + systemServiceCpuThreadTimes.binderThreadCpuTimesUs); + } + + @Test + public void testReaderDelta_nextTime() throws IOException { + int uid = 42; + setupDirectory( + mProcDirectory.toPath().resolve(String.valueOf(uid)), + new int[]{42, 1, 2, 3}, + new int[]{1000, 2000}, + new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}}); + + SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader( + mProcDirectory.toPath(), uid); + reader.setBinderThreadNativeTids(new int[]{1, 3}); + + // First time, populate "last" snapshot + reader.readDelta(); + + FileUtils.deleteContents(mProcDirectory); + setupDirectory( + mProcDirectory.toPath().resolve(String.valueOf(uid)), + new int[]{42, 1, 2, 3}, + new int[]{1000, 2000}, + new int[][]{{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}}); + + // Second time, get the actual delta + SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = + reader.readDelta(); + + assertArrayEquals(new long[]{3100 * 10000, 2500 * 10000}, + systemServiceCpuThreadTimes.threadCpuTimesUs); + assertArrayEquals(new long[]{1800 * 10000, 1400 * 10000}, + systemServiceCpuThreadTimes.binderThreadCpuTimesUs); + } + + private void setupDirectory(Path processPath, int[] threadIds, int[] cpuFrequencies, + int[][] cpuTimes) throws IOException { + // Make /proc/$PID + assertTrue(processPath.toFile().mkdirs()); + + // Make /proc/$PID/task + final Path selfThreadsPath = processPath.resolve("task"); + assertTrue(selfThreadsPath.toFile().mkdirs()); + + // Make thread directories in reverse order, as they are read in order of creation by + // CpuThreadProcReader + for (int i = 0; i < threadIds.length; i++) { + // Make /proc/$PID/task/$TID + final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i])); + assertTrue(threadPath.toFile().mkdirs()); + + // Make /proc/$PID/task/$TID/time_in_state + final OutputStream timeInStateStream = + Files.newOutputStream(threadPath.resolve("time_in_state")); + for (int j = 0; j < cpuFrequencies.length; j++) { + final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n"; + timeInStateStream.write(line.getBytes()); + } + timeInStateStream.close(); + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java new file mode 100644 index 000000000000..ac5443e1c7ce --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2020 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.os; + +import static org.junit.Assert.assertEquals; + +import android.content.Context; +import android.os.BatteryStats; +import android.os.Binder; +import android.os.Process; + +import androidx.annotation.Nullable; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SystemServicePowerCalculatorTest { + + private PowerProfile mProfile; + private MockBatteryStatsImpl mMockBatteryStats; + private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader; + private MockServerCpuThreadReader mMockServerCpuThreadReader; + private SystemServicePowerCalculator mSystemServicePowerCalculator; + + @Before + public void setUp() throws IOException { + Context context = InstrumentationRegistry.getContext(); + mProfile = new PowerProfile(context, true /* forTest */); + mMockServerCpuThreadReader = new MockServerCpuThreadReader(); + mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader(); + mMockBatteryStats = new MockBatteryStatsImpl(new MockClocks()) + .setPowerProfile(mProfile) + .setSystemServerCpuThreadReader(mMockServerCpuThreadReader) + .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader) + .setUserInfoProvider(new MockUserInfoProvider()); + mMockBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0); + mSystemServicePowerCalculator = + new SystemServicePowerCalculator(mProfile, mMockBatteryStats); + } + + @Test + public void testCalculateApp() { + // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total + mMockServerCpuThreadReader.setThreadTimes( + new long[]{30000, 40000, 50000, 60000, 70000, 80000, 90000}, + new long[]{20000, 30000, 40000, 50000, 60000, 70000, 80000}); + + mMockCpuUidFreqTimeReader.setSystemServerCpuTimes( + new long[]{10000, 20000, 30000, 40000, 50000, 60000, 70000} + ); + + mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false); + + int workSourceUid1 = 100; + int workSourceUid2 = 200; + int transactionCode = 42; + + Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); + BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(workSourceUid1, + Binder.class, transactionCode, true /*screenInteractive */); + stat1.incrementalCallCount = 100; + stat1.recordedCallCount = 100; + stat1.cpuTimeMicros = 1000000; + callStats.add(stat1); + + mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats, null); + + callStats.clear(); + BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2, + Binder.class, transactionCode, true /*screenInteractive */); + stat2.incrementalCallCount = 100; + stat2.recordedCallCount = 100; + stat2.cpuTimeMicros = 9000000; + callStats.add(stat2); + + mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats, null); + + mMockBatteryStats.updateSystemServiceCallStats(); + mMockBatteryStats.updateSystemServerThreadStats(); + + BatterySipper app1 = new BatterySipper(BatterySipper.DrainType.APP, + mMockBatteryStats.getUidStatsLocked(workSourceUid1), 0); + mSystemServicePowerCalculator.calculateApp(app1, app1.uidObj, 0, 0, + BatteryStats.STATS_SINCE_CHARGED); + assertEquals(0.27162, app1.systemServiceCpuPowerMah, 0.00001); + + BatterySipper app2 = new BatterySipper(BatterySipper.DrainType.APP, + mMockBatteryStats.getUidStatsLocked(workSourceUid2), 0); + mSystemServicePowerCalculator.calculateApp(app2, app2.uidObj, 0, 0, + BatteryStats.STATS_SINCE_CHARGED); + assertEquals(2.44458, app2.systemServiceCpuPowerMah, 0.00001); + } + + private static class MockKernelCpuUidFreqTimeReader extends + KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader { + private long[] mSystemServerCpuTimes; + + MockKernelCpuUidFreqTimeReader() { + super(/*throttle */false); + } + + void setSystemServerCpuTimes(long[] systemServerCpuTimes) { + mSystemServerCpuTimes = systemServerCpuTimes; + } + + @Override + public boolean perClusterTimesAvailable() { + return true; + } + + @Override + public void readDelta(@Nullable Callback<long[]> cb) { + if (cb != null) { + cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes); + } + } + } + + private static class MockServerCpuThreadReader extends SystemServerCpuThreadReader { + private SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes(); + + MockServerCpuThreadReader() { + super(null); + } + + public void setThreadTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) { + mThreadTimes.threadCpuTimesUs = threadCpuTimesUs; + mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs; + } + + @Override + public SystemServiceCpuThreadTimes readDelta() { + return mThreadTimes; + } + } + + private static class MockUserInfoProvider extends BatteryStatsImpl.UserInfoProvider { + @Nullable + @Override + protected int[] getUserIds() { + return new int[0]; + } + + @Override + public boolean exists(int userId) { + return true; + } + } +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index f834ce798a27..b3c82631011a 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -432,18 +432,6 @@ applications that come with the platform <permission name="android.permission.CAPTURE_AUDIO_OUTPUT" /> <!-- Permissions required for CTS test - AdbManagerTest --> <permission name="android.permission.MANAGE_DEBUGGING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp --> - <permission name="android.car.permission.CAR_DRIVING_STATE" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases --> - <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <permission name="android.car.permission.CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <permission name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <permission name="android.car.permission.CONTROL_APP_BLOCKING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <permission name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java index 3ffb8ea40789..a2ee065cd1cc 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java @@ -23,12 +23,15 @@ import com.google.errorprone.BugPattern; import com.google.errorprone.VisitorState; import com.google.errorprone.bugpatterns.BugChecker; import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher; +import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher; import com.google.errorprone.matchers.Description; import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.Tree; import com.sun.tools.javac.code.Symbol.VarSymbol; import java.util.List; @@ -44,11 +47,20 @@ import java.util.regex.Pattern; name = "AndroidFrameworkUid", summary = "Verifies that PID, UID and user ID arguments aren't crossed", severity = WARNING) -public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher { +public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher, + NewClassTreeMatcher { @Override public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { - final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params(); - final List<? extends ExpressionTree> args = tree.getArguments(); + return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree); + } + + @Override + public Description matchNewClass(NewClassTree tree, VisitorState state) { + return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree); + } + + private Description matchArguments(List<VarSymbol> vars, + List<? extends ExpressionTree> args, Tree tree) { for (int i = 0; i < Math.min(vars.size(), args.size()); i++) { final Flavor varFlavor = getFlavor(vars.get(i)); final Flavor argFlavor = getFlavor(args.get(i)); diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java index 74da94731092..75341fd1f317 100644 --- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java @@ -34,7 +34,7 @@ public class UidCheckerTest { } @Test - public void testTypical() { + public void testTypical_methodInvocation() { compilationHelper .addSourceLines("Example.java", "public abstract class Example {", @@ -57,7 +57,30 @@ public class UidCheckerTest { } @Test - public void testCallingUid() { + public void testTypical_newClass() { + compilationHelper + .addSourceLines("Example.java", + "public abstract class Example {", + " class Bar { Bar(int pid, int uid, int userId) {} }", + " abstract int getUserId();", + " void foo(int pid, int uid, int userId, int unrelated) {", + " new Bar(0, 0, 0);", + " new Bar(pid, uid, userId);", + " new Bar(pid, uid, getUserId());", + " new Bar(unrelated, unrelated, unrelated);", + " // BUG: Diagnostic contains:", + " new Bar(uid, pid, userId);", + " // BUG: Diagnostic contains:", + " new Bar(pid, userId, uid);", + " // BUG: Diagnostic contains:", + " new Bar(getUserId(), 0, 0);", + " }", + "}") + .doTest(); + } + + @Test + public void testCallingUid_methodInvocation() { compilationHelper .addSourceFile("/android/os/Binder.java") .addSourceFile("/android/os/UserHandle.java") @@ -98,4 +121,48 @@ public class UidCheckerTest { "}") .doTest(); } + + + @Test + public void testCallingUid_newClass() { + compilationHelper + .addSourceFile("/android/os/Binder.java") + .addSourceFile("/android/os/UserHandle.java") + .addSourceLines("Example.java", + "import android.os.Binder;", + "import android.os.UserHandle;", + "public abstract class Example {", + " int callingUserId;", + " int callingUid;", + " class Foo { Foo(int callingUserId) {} }", + " class Bar { Bar(int callingUid) {} }", + " void doUserId(int callingUserId) {", + " new Foo(UserHandle.getUserId(Binder.getCallingUid()));", + " new Foo(this.callingUserId);", + " new Foo(callingUserId);", + " // BUG: Diagnostic contains:", + " new Foo(Binder.getCallingUid());", + " // BUG: Diagnostic contains:", + " new Foo(this.callingUid);", + " // BUG: Diagnostic contains:", + " new Foo(callingUid);", + " }", + " void doUid(int callingUserId) {", + " new Bar(Binder.getCallingUid());", + " new Bar(this.callingUid);", + " new Bar(callingUid);", + " // BUG: Diagnostic contains:", + " new Bar(UserHandle.getUserId(Binder.getCallingUid()));", + " // BUG: Diagnostic contains:", + " new Bar(this.callingUserId);", + " // BUG: Diagnostic contains:", + " new Bar(callingUserId);", + " }", + " void doInner() {", + " // BUG: Diagnostic contains:", + " new Foo(UserHandle.getUserId(callingUserId));", + " }", + "}") + .doTest(); + } } diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index 0f6837640524..e76aace601be 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -228,9 +228,12 @@ static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlo static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl) { ScopedUtfChars strSksl(env, sksl); - sk_sp<SkRuntimeEffect> effect = std::get<0>(SkRuntimeEffect::Make(SkString(strSksl.c_str()))); - ThrowIAE_IfNull(env, effect); - + auto result = SkRuntimeEffect::Make(SkString(strSksl.c_str())); + sk_sp<SkRuntimeEffect> effect = std::get<0>(result); + if (!effect) { + const auto& err = std::get<1>(result); + doThrowIAE(env, err.c_str()); + } return reinterpret_cast<jlong>(effect.release()); } diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java index 542737b479e2..ef68814bce84 100644 --- a/location/java/android/location/LocationManagerInternal.java +++ b/location/java/android/location/LocationManagerInternal.java @@ -21,6 +21,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.location.util.identity.CallerIdentity; +import java.util.List; + /** * Location manager local system service interface. * @@ -29,6 +31,16 @@ import android.location.util.identity.CallerIdentity; public abstract class LocationManagerInternal { /** + * Listener for changes in provider enabled state. + */ + public interface ProviderEnabledListener { + /** + * Called when the provider enabled state changes for a particular user. + */ + void onProviderEnabledChanged(String provider, int userId, boolean enabled); + } + + /** * Returns true if the given provider is enabled for the given user. * * @param provider A location provider as listed by {@link LocationManager#getAllProviders()} @@ -38,6 +50,24 @@ public abstract class LocationManagerInternal { public abstract boolean isProviderEnabledForUser(@NonNull String provider, int userId); /** + * Adds a provider enabled listener. The given provider must exist. + * + * @param provider The provider to listen for changes + * @param listener The listener + */ + public abstract void addProviderEnabledListener(String provider, + ProviderEnabledListener listener); + + /** + * Removes a provider enabled listener. The given provider must exist. + * + * @param provider The provider to listen for changes + * @param listener The listener + */ + public abstract void removeProviderEnabledListener(String provider, + ProviderEnabledListener listener); + + /** * Returns true if the given identity is a location provider. * * @param provider The provider to check, or null to check every provider @@ -52,4 +82,10 @@ public abstract class LocationManagerInternal { */ // TODO: there is no reason for this to exist as part of any API. move all the logic into gnss public abstract void sendNiResponse(int notifId, int userResponse); + + /** + * Should only be used by GNSS code. + */ + // TODO: there is no reason for this to exist as part of any API. create a real batching API + public abstract void reportGnssBatchLocations(List<Location> locations); } diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index bb36c2a1fc39..280bd058ef0f 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -32,6 +32,8 @@ import android.util.TimeUtils; import com.android.internal.util.Preconditions; +import java.util.Objects; + /** * A data object that contains quality of service parameters for requests @@ -150,8 +152,6 @@ public final class LocationRequest implements Parcelable { @UnsupportedAppUsage private String mProvider; - // if true, client requests coarse location, if false, client requests fine location - private boolean mCoarseLocation; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private int mQuality; @UnsupportedAppUsage @@ -257,7 +257,6 @@ public final class LocationRequest implements Parcelable { public LocationRequest() { this( /* provider= */ LocationManager.FUSED_PROVIDER, - /* coarseLocation= */ false, /* quality= */ POWER_LOW, /* interval= */ DEFAULT_INTERVAL_MS, /* fastestInterval= */ (long) (DEFAULT_INTERVAL_MS / FASTEST_INTERVAL_FACTOR), @@ -276,7 +275,6 @@ public final class LocationRequest implements Parcelable { public LocationRequest(LocationRequest src) { this( src.mProvider, - src.mCoarseLocation, src.mQuality, src.mInterval, src.mFastestInterval, @@ -293,7 +291,6 @@ public final class LocationRequest implements Parcelable { private LocationRequest( @NonNull String provider, - boolean coarseLocation, int quality, long intervalMs, long fastestIntervalMs, @@ -310,7 +307,6 @@ public final class LocationRequest implements Parcelable { checkQuality(quality); mProvider = provider; - mCoarseLocation = coarseLocation; mQuality = quality; mInterval = intervalMs; mFastestInterval = fastestIntervalMs; @@ -327,20 +323,6 @@ public final class LocationRequest implements Parcelable { } /** - * @hide - */ - public boolean isCoarse() { - return mCoarseLocation; - } - - /** - * @hide - */ - public void setCoarse(boolean coarse) { - mCoarseLocation = coarse; - } - - /** * Set the quality of the request. * * <p>Use with a accuracy constant such as {@link #ACCURACY_FINE}, or a power @@ -720,7 +702,6 @@ public final class LocationRequest implements Parcelable { public LocationRequest createFromParcel(Parcel in) { return new LocationRequest( /* provider= */ in.readString(), - /* coarseLocation= */ in.readBoolean(), /* quality= */ in.readInt(), /* interval= */ in.readLong(), /* fastestInterval= */ in.readLong(), @@ -749,7 +730,6 @@ public final class LocationRequest implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeString(mProvider); - parcel.writeBoolean(mCoarseLocation); parcel.writeInt(mQuality); parcel.writeLong(mInterval); parcel.writeLong(mFastestInterval); @@ -784,6 +764,36 @@ public final class LocationRequest implements Parcelable { } } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + LocationRequest that = (LocationRequest) o; + return mQuality == that.mQuality + && mInterval == that.mInterval + && mFastestInterval == that.mFastestInterval + && mExplicitFastestInterval == that.mExplicitFastestInterval + && mExpireAt == that.mExpireAt + && mExpireIn == that.mExpireIn + && mNumUpdates == that.mNumUpdates + && Float.compare(that.mSmallestDisplacement, mSmallestDisplacement) == 0 + && mHideFromAppOps == that.mHideFromAppOps + && mLocationSettingsIgnored == that.mLocationSettingsIgnored + && mLowPowerMode == that.mLowPowerMode + && mProvider.equals(that.mProvider) + && Objects.equals(mWorkSource, that.mWorkSource); + } + + @Override + public int hashCode() { + return Objects.hash(mProvider, mInterval, mWorkSource); + } + @NonNull @Override public String toString() { @@ -794,7 +804,7 @@ public final class LocationRequest implements Parcelable { if (mQuality != POWER_NONE) { s.append(" interval="); TimeUtils.formatDuration(mInterval, s); - if (mExplicitFastestInterval) { + if (mExplicitFastestInterval && mFastestInterval != mInterval) { s.append(" fastestInterval="); TimeUtils.formatDuration(mFastestInterval, s); } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index c6fb3ef8f694..408f34be6b65 100755 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -4601,36 +4601,41 @@ public class AudioManager { /** * @hide * Volume behavior for an audio device that has no particular volume behavior set. Invalid as - * an argument to {@link #setDeviceVolumeBehavior(int, String, int)}. + * an argument to {@link #setDeviceVolumeBehavior(AudioDeviceAttributes, int)} and should not + * be returned by {@link #getDeviceVolumeBehavior(AudioDeviceAttributes)}. */ public static final int DEVICE_VOLUME_BEHAVIOR_UNSET = -1; /** * @hide * Volume behavior for an audio device where a software attenuation is applied - * @see #setDeviceVolumeBehavior(int, String, int) + * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int) */ + @SystemApi public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; /** * @hide * Volume behavior for an audio device where the volume is always set to provide no attenuation * nor gain (e.g. unit gain). - * @see #setDeviceVolumeBehavior(int, String, int) + * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int) */ + @SystemApi public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1; /** * @hide * Volume behavior for an audio device where the volume is either set to muted, or to provide * no attenuation nor gain (e.g. unit gain). - * @see #setDeviceVolumeBehavior(int, String, int) + * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int) */ + @SystemApi public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2; /** * @hide * Volume behavior for an audio device where no software attenuation is applied, and * the volume is kept synchronized between the host and the device itself through a * device-specific protocol such as BT AVRCP. - * @see #setDeviceVolumeBehavior(int, String, int) + * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int) */ + @SystemApi public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; /** * @hide @@ -4639,8 +4644,9 @@ public class AudioManager { * device-specific protocol (such as for hearing aids), based on the audio mode (e.g. * normal vs in phone call). * @see #setMode(int) - * @see #setDeviceVolumeBehavior(int, String, int) + * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int) */ + @SystemApi public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; /** @hide */ @@ -4687,27 +4693,15 @@ public class AudioManager { /** * @hide * Sets the volume behavior for an audio output device. - * @param deviceType the type of audio device to be affected. Currently only supports - * {@link AudioDeviceInfo#TYPE_HDMI}, {@link AudioDeviceInfo#TYPE_HDMI_ARC}, - * {@link AudioDeviceInfo#TYPE_LINE_DIGITAL} and {@link AudioDeviceInfo#TYPE_AUX_LINE} - * @param deviceAddress the address of the device, if any - * @param deviceVolumeBehavior one of the device behaviors - */ - @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public void setDeviceVolumeBehavior(int deviceType, @Nullable String deviceAddress, - @DeviceVolumeBehavior int deviceVolumeBehavior) { - setDeviceVolumeBehavior(new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT, - deviceType, deviceAddress), deviceVolumeBehavior); - } - - /** - * @hide - * Sets the volume behavior for an audio output device. - * @param device the device to be affected. Currently only supports devices of type - * {@link AudioDeviceInfo#TYPE_HDMI}, {@link AudioDeviceInfo#TYPE_HDMI_ARC}, - * {@link AudioDeviceInfo#TYPE_LINE_DIGITAL} and {@link AudioDeviceInfo#TYPE_AUX_LINE} + * @see #DEVICE_VOLUME_BEHAVIOR_VARIABLE + * @see #DEVICE_VOLUME_BEHAVIOR_FULL + * @see #DEVICE_VOLUME_BEHAVIOR_FIXED + * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE + * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE + * @param device the device to be affected * @param deviceVolumeBehavior one of the device behaviors */ + @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device, @DeviceVolumeBehavior int deviceVolumeBehavior) { @@ -4726,29 +4720,14 @@ public class AudioManager { /** * @hide - * Returns the volume device behavior for the given device type and address - * @param deviceType an audio output device type, as defined in {@link AudioDeviceInfo} - * @param deviceAddress the address of the audio device, if any. - * @return the volume behavior for the device - */ - @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public @DeviceVolumeBehaviorState int getDeviceVolumeBehavior(int deviceType, - @Nullable String deviceAddress) { - // verify arguments - AudioDeviceInfo.enforceValidAudioDeviceTypeOut(deviceType); - return getDeviceVolumeBehavior(new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT, - deviceType, deviceAddress)); - } - - /** - * @hide * Returns the volume device behavior for the given audio device * @param device the audio device * @return the volume behavior for the device */ + @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public @DeviceVolumeBehaviorState int getDeviceVolumeBehavior( - @NonNull AudioDeviceAttributes device) { + public @DeviceVolumeBehavior + int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) { // verify arguments (validity of device type is enforced in server) Objects.requireNonNull(device); // communicate with service diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index 54618a5da3a3..70ef69d8ce72 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -11900,6 +11900,7 @@ package android.content.pm { field public static final int INVALID_ID = -1; // 0xffffffff field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2 field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0 + field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4 field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3 field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1 } diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 385ae278975c..844e929774a1 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -4141,6 +4141,7 @@ package android.media { method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups(); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes); method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); @@ -4159,6 +4160,7 @@ package android.media { method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int requestAudioFocus(@NonNull android.media.AudioFocusRequest, @Nullable android.media.audiopolicy.AudioPolicy); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long); method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]); @@ -4169,6 +4171,11 @@ package android.media { field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1 field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4 field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2 + field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3 + field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4 + field public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2; // 0x2 + field public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1; // 0x1 + field public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; // 0x0 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int STREAM_ASSISTANT = 11; // 0xb field public static final int SUCCESS = 0; // 0x0 } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java index 5dcb9de4755e..a2cd0446d1e8 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java @@ -39,7 +39,7 @@ import javax.inject.Singleton; /** * A class that detects unsafe apps. - * An app is considered safe if is a system app or installed through whitelisted sources. + * An app is considered safe if is a system app or installed through allowed sources. */ @Singleton public class SideLoadedAppDetector { diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java index 5f9665ff7632..0452b83b125f 100644 --- a/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java +++ b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java @@ -172,32 +172,32 @@ public class BarControlPolicy { private static class Filter { private static final String ALL = "*"; - private final ArraySet<String> mWhitelist; - private final ArraySet<String> mBlacklist; + private final ArraySet<String> mToInclude; + private final ArraySet<String> mToExclude; - private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) { - mWhitelist = whitelist; - mBlacklist = blacklist; + private Filter(ArraySet<String> toInclude, ArraySet<String> toExclude) { + mToInclude = toInclude; + mToExclude = toExclude; } boolean matches(String packageName) { if (packageName == null) return false; - if (onBlacklist(packageName)) return false; - return onWhitelist(packageName); + if (toExclude(packageName)) return false; + return toInclude(packageName); } - private boolean onBlacklist(String packageName) { - return mBlacklist.contains(packageName) || mBlacklist.contains(ALL); + private boolean toExclude(String packageName) { + return mToExclude.contains(packageName) || mToExclude.contains(ALL); } - private boolean onWhitelist(String packageName) { - return mWhitelist.contains(ALL) || mWhitelist.contains(packageName); + private boolean toInclude(String packageName) { + return mToInclude.contains(ALL) || mToInclude.contains(packageName); } void dump(PrintWriter pw) { pw.print("Filter["); - dump("whitelist", mWhitelist, pw); pw.print(','); - dump("blacklist", mBlacklist, pw); pw.print(']'); + dump("toInclude", mToInclude, pw); pw.print(','); + dump("toExclude", mToExclude, pw); pw.print(']'); } private void dump(String name, ArraySet<String> set, PrintWriter pw) { @@ -221,18 +221,18 @@ public class BarControlPolicy { // e.g. "com.package1", or "com.android.systemui, com.android.keyguard" or "*" static Filter parse(String value) { if (value == null) return null; - ArraySet<String> whitelist = new ArraySet<String>(); - ArraySet<String> blacklist = new ArraySet<String>(); + ArraySet<String> toInclude = new ArraySet<String>(); + ArraySet<String> toExclude = new ArraySet<String>(); for (String token : value.split(",")) { token = token.trim(); if (token.startsWith("-") && token.length() > 1) { token = token.substring(1); - blacklist.add(token); + toExclude.add(token); } else { - whitelist.add(token); + toInclude.add(token); } } - return new Filter(whitelist, blacklist); + return new Filter(toInclude, toExclude); } } diff --git a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java index d769cacadf1d..86b86d482eee 100644 --- a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java +++ b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java @@ -63,7 +63,7 @@ public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestC private static final String TAG = "AAA++VerifyTest"; - private static final Class[] BASE_CLS_WHITELIST = { + private static final Class[] BASE_CLS_TO_INCLUDE = { SysuiTestCase.class, SysuiBaseFragmentTest.class, }; @@ -85,7 +85,7 @@ public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestC if (!isTestClass(cls)) continue; boolean hasParent = false; - for (Class<?> parent : BASE_CLS_WHITELIST) { + for (Class<?> parent : BASE_CLS_TO_INCLUDE) { if (parent.isAssignableFrom(cls)) { hasParent = true; break; @@ -129,7 +129,7 @@ public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestC } private String getClsStr() { - return TextUtils.join(",", Arrays.asList(BASE_CLS_WHITELIST) + return TextUtils.join(",", Arrays.asList(BASE_CLS_TO_INCLUDE) .stream().map(cls -> cls.getSimpleName()).toArray()); } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java index 31f1170c9603..f77294e37b98 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java @@ -19,6 +19,8 @@ package com.android.systemui.car.voicerecognition; import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.INVALID_VALUE; import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.VOICE_RECOGNITION_STARTED; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -40,11 +42,13 @@ import com.android.systemui.car.CarSystemUiTest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; @CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest +// TODO(b/162866441): Refactor to use the Executor pattern instead. public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; @@ -52,13 +56,15 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier; private TestableLooper mTestableLooper; + private Handler mHandler; private Handler mTestHandler; private BluetoothDevice mBluetoothDevice; @Before public void setUp() throws Exception { mTestableLooper = TestableLooper.get(this); - mTestHandler = spy(new Handler(mTestableLooper.getLooper())); + mHandler = new Handler(mTestableLooper.getLooper()); + mTestHandler = spy(mHandler); mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( BLUETOOTH_REMOTE_ADDRESS); mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier( @@ -74,8 +80,14 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); - - verify(mTestHandler).post(any()); + waitForIdleSync(); + + mHandler.post(() -> { + ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class); + verify(mTestHandler).post(argumentCaptor.capture()); + assertThat(argumentCaptor.getValue()).isNotNull(); + assertThat(argumentCaptor.getValue()).isNotEqualTo(this); + }); } @Test @@ -86,8 +98,11 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); + waitForIdleSync(); - verify(mTestHandler, never()).post(any()); + mHandler.post(() -> { + verify(mTestHandler, never()).post(any()); + }); } @Test @@ -97,8 +112,11 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); + waitForIdleSync(); - verify(mTestHandler, never()).post(any()); + mHandler.post(() -> { + verify(mTestHandler, never()).post(any()); + }); } @Test @@ -108,7 +126,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); + waitForIdleSync(); - verify(mTestHandler, never()).post(any()); + mHandler.post(() -> { + verify(mTestHandler, never()).post(any()); + }); } } diff --git a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeView.java b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeView.java index 276d55ee9a5e..9fe7ab655e46 100644 --- a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeView.java +++ b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeView.java @@ -26,7 +26,7 @@ import android.os.Message; import android.view.View; /** - * Dummy view to emulate stuff an OEM may want to do. + * Fake view to emulate stuff an OEM may want to do. */ public class FakeView extends View { static final long TICK_DELAY = 30*1000; // 30 seconds diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java index a12aa83e9d4b..a08f566b8375 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java @@ -19,9 +19,9 @@ package com.android.settingslib.drawer; import android.os.Bundle; /** - * A controller that manages event for master switch. + * A controller that manages event for Primary switch. */ -public abstract class MasterSwitchController extends SwitchController { +public abstract class PrimarySwitchController extends SwitchController { @Override protected final MetaData getMetaData() { diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java index 73f1a904b04b..f2b3e30dc252 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java @@ -88,7 +88,7 @@ public abstract class SwitchesProvider extends ContentProvider { controller.setAuthority(mAuthority); mControllerMap.put(key, controller); - if (!(controller instanceof MasterSwitchController)) { + if (!(controller instanceof PrimarySwitchController)) { mSwitchDataList.add(controller.getBundle()); } }); @@ -116,7 +116,7 @@ public abstract class SwitchesProvider extends ContentProvider { switch (method) { case METHOD_GET_SWITCH_DATA: - if (!(controller instanceof MasterSwitchController)) { + if (!(controller instanceof PrimarySwitchController)) { return controller.getBundle(); } break; diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 704d264d1983..6751fa4583c5 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Wys Bluetooth-toestelle sonder name"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Deaktiveer absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiveer Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbeterde konnektiwiteit"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-weergawe"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Kies Bluetooth AVRCP-weergawe"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-weergawe"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 585924d7efb9..470e780fc049 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"የብሉቱዝ መሣሪያዎችን ያለ ስሞች አሳይ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ፍጹማዊ ድምፅን አሰናክል"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheን አንቃ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"የተሻሻለ ተገናኝነት"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"የብሉቱዝ AVRCP ስሪት"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"የብሉቱዝ AVRCP ስሪት ይምረጡ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"የብሉቱዝ MAP ስሪት"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 39777cd5f881..9acfa0da7747 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -152,7 +152,7 @@ <string name="user_guest" msgid="6939192779649870792">"ضيف"</string> <string name="unknown" msgid="3544487229740637809">"غير معروف"</string> <string name="running_process_item_user_label" msgid="3988506293099805796">"المستخدم: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> - <string name="launch_defaults_some" msgid="3631650616557252926">"تم تعيين بعض الإعدادات التلقائية"</string> + <string name="launch_defaults_some" msgid="3631650616557252926">"تم ضبط بعض الإعدادات التلقائية"</string> <string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم تعيين إعدادات تلقائية"</string> <string name="tts_settings" msgid="8130616705989351312">"إعدادات تحويل النص إلى كلام"</string> <string name="tts_settings_title" msgid="7602210956640483039">"تحويل النص إلى كلام"</string> @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"عرض أجهزة البلوتوث بدون أسماء"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"إيقاف مستوى الصوت المطلق"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"تفعيل Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"إمكانية اتصال محسّن"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"إصدار Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"اختيار إصدار Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"إصدار Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index e0455cb5f19c..f993dba39476 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামবিহীন ব্লুটুথ ডিভাইচসমূহ দেখুৱাওক"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"পূৰ্ণ মাত্ৰাৰ ভলিউম অক্ষম কৰক"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche সক্ষম কৰক"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"উন্নত সংযোগ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP সংস্কৰণ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP সংস্কৰণ বাছনি কৰক"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP সংস্কৰণ"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 6565d530f6a1..6a506614f970 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth cihazlarını adsız göstərin"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Mütləq səs həcmi deaktiv edin"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'ni aktiv edin"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Təkmilləşdirilmiş Bağlantı"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Versiya"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Versiyasını seçin"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Versiyası"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 3ced29b47dd6..cf988ab3589f 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući glavno podešavanje jačine zvuka"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšano povezivanje"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija Bluetooth AVRCP-a"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izaberite verziju Bluetooth AVRCP-a"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija Bluetooth MAP-a"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 01d7682416fa..8f71509772fc 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Паказваць прылады Bluetooth без назваў"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Адключыць абсалютны гук"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Уключыць Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Палепшанае падключэнне"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выбраць версію Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index d042c0f89b1e..747cb266b6de 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показване на устройствата с Bluetooth без имена"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Деактивиране на пълната сила на звука"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Активиране на Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Подобрена свързаност"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия на AVRCP за Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Избиране на версия на AVRCP за Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP версия за Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 2db23f73e683..87f3b7a4f2e8 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামহীন ব্লুটুথ ডিভাইসগুলি দেখুন"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"চূড়ান্ত ভলিউম অক্ষম করুন"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ফিচার চালু করুন"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"কানেক্টিভিটি উন্নত করা হয়েছে"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP ভার্সন"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP ভার্সন বেছে নিন"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP ভার্সন"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index f26fe9d58533..e329c993eedb 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući apsolutnu jačinu zvuka"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšana povezivost"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP verzija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite Bluetooth AVRCP verziju"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP verzija"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 3ca9d5272d48..5ffdacd19c22 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra els dispositius Bluetooth sense el nom"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desactiva el volum absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activa Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivitat millorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versió AVRCP de Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versió AVRCP de Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versió MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index a5532e0bff02..0aef99feb04f 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovat zařízení Bluetooth bez názvů"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zakázat absolutní hlasitost"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Zapnout funkci Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Lepší připojování"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verze profilu Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vyberte verzi profilu Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verze MAP pro Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 8ca22d7ba797..98068cb172fa 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheder uden navne"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Deaktiver absolut lydstyrke"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivér Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version for Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vælg AVRCP-version for Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version for Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 6b0ae2e24bb6..0837ad33ab06 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-Geräte ohne Namen anzeigen"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Absolute Lautstärkeregelung deaktivieren"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bluetooth-Gabeldorsche aktivieren"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbesserte Konnektivität"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-Version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP-Version auswählen"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-Version"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 4d7c88287f72..1f9d97764016 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Εμφάνιση συσκευών Bluetooth χωρίς ονόματα"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Απενεργοποίηση απόλυτης έντασης"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ενεργοποίηση Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Βελτιωμένη συνδεσιμότητα"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Έκδοση AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Επιλογή έκδοσης AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Έκδοση MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index cc3b2aa33695..abe61a97096c 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index a9f039a6522c..959ad46bcf61 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index cc3b2aa33695..abe61a97096c 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index cc3b2aa33695..abe61a97096c 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 41c20e0c915f..738dd2a2ba31 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Version"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 287a1aca2fbd..d1e4fb5607a8 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inhabilitar volumen absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividad mejorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de AVRCP del Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión de AVRCP del Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 9d3455766f6b..9ad71e2766a3 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inhabilitar volumen absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividad mejorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión AVRCP de Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión AVRCP de Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index d003ef0b9c71..14d3b5782862 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Kuva ilma nimedeta Bluetoothi seadmed"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Keela absoluutne helitugevus"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Luba Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Täiustatud ühenduvus"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothi AVRCP versioon"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valige Bluetoothi AVRCP versioon"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothi MAP-i versioon"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 0042321a3654..dcdadbdf3454 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Erakutsi Bluetooth bidezko gailuak izenik gabe"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desgaitu bolumen absolutua"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gaitu Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Konexio hobeak"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP bertsioa"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Hautatu Bluetooth AVRCP bertsioa"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAParen bertsioa"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 1c0881531f89..f3b22d355b77 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"نمایش دستگاههای بلوتوث بدون نام"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"غیرفعال کردن میزان صدای مطلق"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"فعال کردن Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"اتصال بهبودیافته"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"نسخه AVRCP بلوتوث"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"انتخاب نسخه AVRCP بلوتوث"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"نسخه MAP بلوتوث"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 3945e5542eb7..3d28f1d0a1f5 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Näytä nimettömät Bluetooth-laitteet"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Poista yleinen äänenvoimakkuuden säätö käytöstä"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ota Gabeldorsche käyttöön"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Parannetut yhteydet"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothin AVRCP-versio"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valitse Bluetoothin AVRCP-versio"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothin MAP-versio"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 140d4cee6ad7..87d3de16f216 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Désactiver le volume absolu"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer le Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivité améliorée"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version du profil Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version du profil Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version du profil Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 1b1ae8ed55d4..ddf2bc0991a4 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -251,13 +251,12 @@ <string name="wifi_display_certification" msgid="1805579519992520381">"Certification affichage sans fil"</string> <string name="wifi_verbose_logging" msgid="1785910450009679371">"Autoriser l\'enregistrement d\'infos Wi-Fi détaillées"</string> <string name="wifi_scan_throttling" msgid="2985624788509913617">"Limiter la recherche Wi‑Fi"</string> - <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Chgt aléatoire d\'adresse MAC sur Wi-Fi"</string> + <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Chgt aléatoire d\'adresse MAC en Wi-Fi"</string> <string name="mobile_data_always_on" msgid="8275958101875563572">"Données mobiles toujours actives"</string> <string name="tethering_hardware_offload" msgid="4116053719006939161">"Accélération matérielle pour le partage de connexion"</string> <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Désactiver le volume absolu"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivité améliorée"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version Bluetooth MAP"</string> @@ -284,7 +283,7 @@ <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afficher les options pour la certification de l\'affichage sans fil"</string> <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Détailler les infos Wi-Fi, afficher par RSSI de SSID dans l\'outil de sélection Wi-Fi"</string> <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Réduit la décharge de la batterie et améliore les performances du réseau"</string> - <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Lorsque ce mode est activé, l\'adresse e-mail MAC de cet appareil peut changer lors de chaque connexion à un réseau pour lequel le changement aléatoire d\'adresse MAC est activé."</string> + <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Lorsque ce mode est activé, l\'adresse MAC de cet appareil peut changer lors de chaque connexion à un réseau Wi-Fi pour lequel le changement aléatoire d\'adresse MAC est activé"</string> <string name="wifi_metered_label" msgid="8737187690304098638">"Facturé à l\'usage"</string> <string name="wifi_unmetered_label" msgid="6174142840934095093">"Non facturé à l\'usage"</string> <string name="select_logd_size_title" msgid="1604578195914595173">"Tailles des tampons de l\'enregistreur"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index f9d57c453f69..af430991083d 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sen nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desactivar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade mellorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona a versión de Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index aa1f9605b07d..3261f69b1105 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"નામ વિનાના બ્લૂટૂથ ઉપકરણો બતાવો"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ચોક્કસ વૉલ્યૂમને અક્ષમ કરો"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ચાલુ કરો"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"વિસ્તૃત કનેક્ટિવિટી"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"બ્લૂટૂથ AVRCP સંસ્કરણ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"બ્લૂટૂથ AVRCP સંસ્કરણ પસંદ કરો"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"બ્લૂટૂથ MAP વર્ઝન"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 9b6a27ae8620..904a70e73958 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"बिना नाम वाले ब्लूटूथ डिवाइस दिखाएं"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ब्लूटूथ से आवाज़ के नियंत्रण की सुविधा रोकें"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche चालू करें"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"कनेक्टिविटी बेहतर बनाएं"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ एवीआरसीपी वर्शन"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP वर्शन चुनें"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ का MAP वर्शन"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 14e3330a1670..3edc4527fb01 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući apsolutnu glasnoću"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšana povezivost"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija AVRCP-a za Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite verziju AVRCP-a za Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija MAP-a za Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index d16ff03b0903..fec2dd6d523f 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Név nélküli Bluetooth-eszközök megjelenítése"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Abszolút hangerő funkció letiltása"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"A Gabeldorsche engedélyezése"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"A Bluetooth AVRCP-verziója"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"A Bluetooth AVRCP-verziójának kiválasztása"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"A Bluetooth MAP-verziója"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index b010b504b00c..f219d248e641 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ցուցադրել Bluetooth սարքերն առանց անունների"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Անջատել ձայնի բացարձակ ուժգնությունը"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Միացնել Gabeldorsche-ը"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Տվյալների լավացված փոխանակում"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP տարբերակը"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Ընտրել Bluetooth AVRCP տարբերակը"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ի տարբերակ"</string> diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml index 37cf189f26c0..3ab50cc948eb 100644 --- a/packages/SettingsLib/res/values-in/arrays.xml +++ b/packages/SettingsLib/res/values-in/arrays.xml @@ -31,7 +31,7 @@ <item msgid="7852381437933824454">"Memutus sambungan..."</item> <item msgid="5046795712175415059">"Sambungan terputus"</item> <item msgid="2473654476624070462">"Gagal"</item> - <item msgid="9146847076036105115">"Dicekal"</item> + <item msgid="9146847076036105115">"Diblokir"</item> <item msgid="4543924085816294893">"Menghindari sambungan buruk untuk sementara"</item> </string-array> <string-array name="wifi_status_with_ssid"> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 42ccd5310913..a6f88465a673 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -180,8 +180,8 @@ <string name="tts_engine_settings_button" msgid="477155276199968948">"Luncurkan setelan mesin"</string> <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Mesin yang dipilih"</string> <string name="tts_general_section_title" msgid="8919671529502364567">"Umum"</string> - <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Setel ulang tinggi nada ucapan"</string> - <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Setel ulang tinggi nada diucapkannya teks menjadi default."</string> + <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Reset tinggi nada ucapan"</string> + <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Reset tinggi nada diucapkannya teks menjadi default."</string> <string-array name="tts_rate_entries"> <item msgid="9004239613505400644">"Sangat lambat"</item> <item msgid="1815382991399815061">"Lambat"</item> @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tampilkan perangkat Bluetooth tanpa nama"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Nonaktifkan volume absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktifkan Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Konektivitas Yang Disempurnakan"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 0ebc341a9541..caf2323813ca 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Sýna Bluetooth-tæki án heita"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Slökkva á samstillingu hljóðstyrks"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Virkja Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Aukin tengigeta"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-útgáfa"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velja Bluetooth AVRCP-útgáfu"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-útgáfa"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 50fdfc9a9136..8d18727e384c 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra dispositivi Bluetooth senza nome"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disattiva volume assoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Attiva Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connettività migliorata"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versione Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Seleziona versione Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versione Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index fb7d00f866b1..fff881c46433 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"הצגת מכשירי Bluetooth ללא שמות"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"השבת עוצמת קול מוחלטת"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"הפעלת Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"קישוריות משופרת"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth גרסה AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"בחר Bluetooth גרסה AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"גרסת Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 3537ceaf8204..5e579b758f09 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth デバイスを名前なしで表示"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"絶対音量を無効にする"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche を有効にする"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"接続強化"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP バージョン"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP バージョンを選択する"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP バージョン"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 9b671a864e15..1b5fae9781b9 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-მოწყობილობების ჩვენება სახელების გარეშე"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ხმის აბსოლუტური სიძლიერის გათიშვა"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-ის ჩართვა"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"კავშირის გაძლიერებული შესაძლებლობა"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-ის AVRCP-ის ვერსია"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"აირჩიეთ Bluetooth-ის AVRCP-ის ვერსია"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ის ვერსია"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 279aca0731f7..9c290e90dd8d 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth құрылғыларын атаусыз көрсету"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Абсолютті дыбыс деңгейін өшіру"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын іске қосу"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Жетілдірілген байланыс"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP нұсқасы"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP нұсқасын таңдау"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP нұсқасы"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 03065e896da2..2878db192d18 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"បង្ហាញឧបករណ៍ប្ល៊ូធូសគ្មានឈ្មោះ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"បិទកម្រិតសំឡេងលឺខ្លាំង"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"បើក Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ការតភ្ជាប់ដែលបានធ្វើឱ្យប្រសើរឡើង"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"កំណែប្ល៊ូធូស AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ជ្រើសរើសកំណែប្ល៊ូធូស AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"កំណែប៊្លូធូស MAP"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 71c5e491d927..7b010564effa 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ಹೆಸರುಗಳಿಲ್ಲದ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ತೋರಿಸಿ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ಸಂಪೂರ್ಣ ವಾಲ್ಯೂಮ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ವರ್ಧಿತ ಸಂಪರ್ಕ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿಯನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ಬ್ಲೂಟೂತ್ MAP ಆವೃತ್ತಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 5d82eae2fcba..696ed2955daa 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"이름이 없는 블루투스 기기 표시"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"절대 볼륨 사용 안함"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche 사용 설정"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"향상된 연결"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"블루투스 AVRCP 버전"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"블루투스 AVRCP 버전 선택"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"블루투스 MAP 버전"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 2702392c108f..c4b5f7e7477f 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Аталышсыз Bluetooth түзмөктөрү көрсөтүлсүн"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Үндүн абсолюттук деңгээли өчүрүлсүн"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын иштетүү"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Жакшыртылган туташуу"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP версиясы"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP версиясын тандоо"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP версиясы"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 7a2c338ef008..a72861ee6aac 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ສະແດງອຸປະກອນ Bluetooth ທີ່ບໍ່ມີຊື່"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ປິດໃຊ້ລະດັບສຽງສົມບູນ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ເປີດໃຊ້ Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ການເຊື່ອມຕໍ່ທີ່ເສີມແຕ່ງແລ້ວ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ເວີຊັນ Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ເລືອກເວີຊັນ Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ເວີຊັນ Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index b73aa66ae54b..c72bf21d8b29 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rodyti „Bluetooth“ įrenginius be pavadinimų"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Išjungti didžiausią garsą"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Įgalinti „Gabeldorsche“"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Patobulintas ryšys"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"„Bluetooth“ AVRCP versija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pasirinkite „Bluetooth“ AVRCP versiją"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"„Bluetooth“ MRK versija"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 8e5d24cfa81a..d95e57df320e 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rādīt Bluetooth ierīces bez nosaukumiem"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Atspējot absolūto skaļumu"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Iespējot Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Uzlabota savienojamība"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Atlasiet Bluetooth AVRCP versiju"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versija"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 34299d801946..fb7b63410a5f 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажувај уреди со Bluetooth без имиња"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Оневозможете апсолутна јачина на звук"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Овозможи Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Подобрена поврзливост"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изберете верзија Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија на Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index c95f8bf2fe39..3c281c8d972f 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"പേരില്ലാത്ത Bluetooth ഉപകരണങ്ങൾ കാണിക്കുക"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"അബ്സൊല്യൂട്ട് വോളിയം പ്രവർത്തനരഹിതമാക്കുക"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche പ്രവർത്തനക്ഷമമാക്കുക"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"മെച്ചപ്പെടുത്തിയ കണക്റ്റിവിറ്റി"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP പതിപ്പ്"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP പതിപ്പ് തിരഞ്ഞെടുക്കുക"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP പതിപ്പ്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 8407db6d3a08..37fc5b47619c 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Нэргүй Bluetooth төхөөрөмжийг харуулах"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Үнэмлэхүй дууны түвшинг идэвхгүй болгох"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-г идэвхжүүлэх"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Сайжруулсан холболт"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP хувилбар"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP хувилбарыг сонгох"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP хувилбар"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index c50f365f68e3..360f15897ebe 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नावांशिवाय ब्लूटूथ डिव्हाइस दाखवा"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"संपूर्ण आवाज बंद करा"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"गाबलडॉर्ष सुरू करा"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"वर्धित कनेक्टिव्हिटी"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ AVRCP आवृत्ती"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP आवृत्ती निवडा"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ MAP आवृत्ती"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index a0a434f05959..68356df25ef9 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tunjukkan peranti Bluetooth tanpa nama"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Lumpuhkan kelantangan mutlak"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Dayakan Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Kesambungan Dipertingkat"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index fa499293ceab..3729a8352abd 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"အမည်မရှိသော ဘလူးတုသ်စက်ပစ္စည်းများကို ပြသရန်"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ပကတိ အသံနှုန်း သတ်မှတ်ချက် ပိတ်ရန်"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ကို ဖွင့်ရန်"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"အရည်အသွေးမြှင့်တင်ထားသော ချိတ်ဆက်နိုင်မှု"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ဘလူးတုသ် AVRCP ဗားရှင်း"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ဘလူးတုသ် AVRCP ဗားရှင်းကို ရွေးပါ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ဘလူးတုသ် MAP ဗားရှင်း"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index aeaba31691f9..0e0e7617b06d 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheter uten navn"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Slå av funksjonen for absolutt volum"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiver Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Forbedret tilkobling"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-versjon"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velg Bluetooth AVRCP-versjon"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-versjon"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 4a2c1719aeb7..762d8304dd64 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नामकरण नगरिएका ब्लुटुथ यन्त्रहरू देखाउनुहोस्"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"निरपेक्ष आवाज असक्षम गर्नुहोस्"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche सक्षम पार्नुहोस्"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"परिष्कृत जडान"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लुटुथको AVRCP संस्करण"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लुटुथको AVRCP संस्करण चयन गर्नुहोस्"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लुटुथको MAP संस्करण"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 32cc39ec5b4f..83b72e925fe1 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-apparaten zonder namen weergeven"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Absoluut volume uitschakelen"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche inschakelen"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbeterde connectiviteit"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-AVRCP-versie"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth-AVRCP-versie selecteren"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-versie voor bluetooth"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 8e5bf25a19ca..d200f502879e 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ବ୍ଲୁଟୂଥ୍ ଡିଭାଇସ୍ଗୁଡ଼ିକୁ ନାମ ବିନା ଦେଖନ୍ତୁ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ପୂର୍ଣ୍ଣ ଭଲ୍ୟୁମ୍ ଅକ୍ଷମ କରନ୍ତୁ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ଗାବେଲ୍ଡୋର୍ସ ସକ୍ରିୟ କରନ୍ତୁ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ଏନହାନ୍ସଡ୍ କନେକ୍ଟିଭିଟି"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ବ୍ଲୁଟୂଥ୍ AVRCP ଭର୍ସନ୍"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ବ୍ଲୁଟୂଥ୍ AVRCP ଭର୍ସନ୍"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ବ୍ଲୁଟୁଥ୍ MAP ସଂସ୍କରଣ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 15700130cf78..354ee126d9af 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ਅਨਾਮ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਦਿਖਾਓ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ਪੂਰਨ ਅਵਾਜ਼ ਨੂੰ ਬੰਦ ਕਰੋ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ਨੂੰ ਚਾਲੂ ਕਰੋ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ਵਿਸਤ੍ਰਿਤ ਕਨੈਕਟੀਵਿਟੀ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ ਚੁਣੋ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP ਵਰਜਨ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 1120c504717f..095412c94502 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Pokaż urządzenia Bluetooth bez nazw"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Wyłącz głośność bezwzględną"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Włącz Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Lepsza obsługa połączeń"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Wersja AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Wybierz wersję AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Wersja MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 4214a2720813..895a9872f4d4 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade melhorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 508cbfccffe9..2d9f037297fb 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar o Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conetividade melhorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão de Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão de Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão do MAP do Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 4214a2720813..895a9872f4d4 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade melhorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 663d3f702ae5..728db174cd7b 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afișați dispozitivele Bluetooth fără nume"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Dezactivați volumul absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activați Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectivitate îmbunătățită"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versiunea AVRCP pentru Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selectați versiunea AVRCP pentru Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versiunea MAP pentru Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 9c0a2f5beb8b..ff2115ee3ff0 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показывать Bluetooth-устройства без названий"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Отключить абсолютный уровень громкости"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Включить Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Улучшенный обмен данными"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выберите версию Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версия Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 8d0e93e4b5f7..a883cc60d8e1 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"නම් නොමැති බ්ලූටූත් උපාංග පෙන්වන්න"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"නිරපේක්ෂ හඩ පරිමාව අබල කරන්න"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche සබල කරන්න"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"වැඩිදියුණු කළ සබැඳුම් හැකියාව"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"බ්ලූටූත් AVRCP අනුවාදය"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"බ්ලූටූත් AVRCP අනුවාදය තෝරන්න"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP අනුවාදය"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index f39a741e3a4b..05c63795382b 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovať zariadenia Bluetooth bez názvov"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zakázať absolútnu hlasitosť"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Povoliť Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Zlepšené možnosti pripojenia"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzia rozhrania Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zvoľte verziu rozhrania Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzia profilu Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 233c8e48635a..fd216e83d927 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži naprave Bluetooth brez imen"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogočanje absolutne glasnosti"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogoči Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Izboljšana povezljivost"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Različica profila AVRCP za Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izberite različico profila AVRCP za Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Različica profila MAP za Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 6af1062a98f8..002c7fcda363 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Shfaq pajisjet me Bluetooth pa emra"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Çaktivizo volumin absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivizo Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Lidhshmëria e përmirësuar"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versioni AVRCP i Bluetooth-it"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zgjidh versionin AVRCP të Bluetooth-it"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versioni MAP i Bluetooth-it"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 74c2aec7c174..25a1beb7af68 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажи Bluetooth уређаје без назива"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Онемогући главно подешавање јачине звука"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Омогући Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Побољшано повезивање"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP-а"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изаберите верзију Bluetooth AVRCP-а"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија Bluetooth MAP-а"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index fe1b0a856802..352cb0ab7d19 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Visa namnlösa Bluetooth-enheter"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inaktivera Absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivera Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Förbättrad anslutning"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version för Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Välj AVRCP-version för Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version för Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 5c80627003cd..d2891a06402a 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Onyesha vifaa vya Bluetooth visivyo na majina"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zima sauti kamili"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Washa Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Muunganisho Ulioboreshwa"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Toleo la Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chagua Toleo la Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Toleo la Ramani ya Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 241644f631d3..7837dd8d46df 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"பெயர்கள் இல்லாத புளூடூத் சாதனங்களைக் காட்டு"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"அப்சல்யூட் ஒலியளவு அம்சத்தை முடக்கு"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheவை இயக்கு"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"மேம்படுத்தப்பட்ட இணைப்புநிலை"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"புளூடூத் AVRCP பதிப்பு"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"புளூடூத் AVRCP பதிப்பைத் தேர்ந்தெடு"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"புளூடூத்தின் MAP பதிப்பு"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index a9ec2ea95ac5..60001a0b0ecd 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"పేర్లు లేని బ్లూటూత్ పరికరాలు చూపించు"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"సంపూర్ణ వాల్యూమ్ను నిలిపివేయి"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheను ఎనేబుల్ చేయి"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"మెరుగైన కనెక్టివిటీ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"బ్లూటూత్ AVRCP వెర్షన్"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"బ్లూటూత్ AVRCP సంస్కరణను ఎంచుకోండి"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"బ్లూటూత్ MAP వెర్షన్"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index b8343c6a82e7..defc33ee9233 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"แสดงอุปกรณ์บลูทูธที่ไม่มีชื่อ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ปิดใช้การควบคุมระดับเสียงของอุปกรณ์อื่น"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"เปิดใช้ Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"การเชื่อมต่อที่ปรับปรุงแล้ว"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"เวอร์ชันของบลูทูธ AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"เลือกเวอร์ชันของบลูทูธ AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"เวอร์ชัน MAP ของบลูทูธ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 8aeb39256748..5d4e9752fd93 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ipakita ang mga Bluetooth device na walang pangalan"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"I-disable ang absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"I-enable ang Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Pinagandang Pagkakonekta"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bersyon ng AVRCP ng Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pumili ng Bersyon ng AVRCP ng Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bersyon ng MAP ng Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index e6d938047fcf..f01f3faed73e 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Adsız Bluetooth cihazlarını göster"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Mutlak sesi iptal et"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'yi etkileştir"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Gelişmiş Bağlantı"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Sürümü"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Sürümünü seçin"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Sürümü"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index cf1fafd8f190..9ca2f062127a 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показувати пристрої Bluetooth без назв"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Вимкнути абсолютну гучність"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Увімкнути Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Покращене з\'єднання"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Виберіть версію Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index b7fbe6fad392..8953f50078f1 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"بغیر نام والے بلوٹوتھ آلات دکھائیں"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"مطلق والیوم کو غیر فعال کریں"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche فعال کریں"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"بہتر کردہ کنیکٹوٹی"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"بلوٹوتھ AVRCP ورژن"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"بلوٹوتھ AVRCP ورژن منتخب کریں"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"بلوٹوتھ MAP ورژن"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index f81731ab2723..f25b3ac3c37f 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth qurilmalarini nomlarisiz ko‘rsatish"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Tovush balandligining mutlaq darajasini faolsizlantirish"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche funksiyasini yoqish"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Kuchaytirilgan aloqa"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versiyasi"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP versiyasini tanlang"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versiyasi"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index b7ccf8d85677..b5798f31d0f9 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Hiển thị các thiết bị Bluetooth không có tên"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Vô hiệu hóa âm lượng tuyệt đối"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bật tính năng Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Kết nối nâng cao"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Phiên bản Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chọn phiên bản Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Phiên bản Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 75c1333c37c1..c4dcfff1b579 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"显示没有名称的蓝牙设备"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用绝对音量功能"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"启用“Gabeldorsche”"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"增强连接性"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"蓝牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"选择蓝牙 AVRCP 版本"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"蓝牙 MAP 版本"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 26ddfb13717b..e04651cac5f6 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用絕對音量功能"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"強化連線功能"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選擇藍牙 AVRCP 版本"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 72ea0439b1f6..a1ae6b6c27a1 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用絕對音量功能"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"加強型連線"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選取藍牙 AVRCP 版本"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 6b8739fd017a..2dafad8114d8 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bonisa amadivayisi e-Bluetooth ngaphandle kwamagama"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Khubaza ivolumu ngokuphelele"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Nika amandla i-Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Ukuxhumeka Okuthuthukisiwe"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Inguqulo ye-Bluetooth ye-AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Khetha inguqulo ye-Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Inguqulo ye-Bluetooth MAP"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java index f757aa4e4dab..b29595eab5c7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java @@ -24,7 +24,7 @@ import androidx.preference.PreferenceScreen; import com.android.settingslib.core.AbstractPreferenceController; /** - * This controller is used handle changes for the master switch in the developer options page. + * This controller is used handle changes for the primary switch in the developer options page. * * All Preference Controllers that are a part of the developer options page should inherit this * class. diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java index 3c647a7ec465..c501b3aab4d4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java @@ -34,44 +34,50 @@ import com.android.internal.telephony.SmsApplication; import com.android.internal.util.ArrayUtils; /** - * Handles getting/changing the whitelist for the exceptions to battery saving features. + * Handles getting/changing the allowlist for the exceptions to battery saving features. */ -public class PowerWhitelistBackend { +public class PowerAllowlistBackend { - private static final String TAG = "PowerWhitelistBackend"; + private static final String TAG = "PowerAllowlistBackend"; private static final String DEVICE_IDLE_SERVICE = "deviceidle"; - private static PowerWhitelistBackend sInstance; + private static PowerAllowlistBackend sInstance; private final Context mAppContext; private final IDeviceIdleController mDeviceIdleService; - private final ArraySet<String> mWhitelistedApps = new ArraySet<>(); - private final ArraySet<String> mSysWhitelistedApps = new ArraySet<>(); + private final ArraySet<String> mAllowlistedApps = new ArraySet<>(); + private final ArraySet<String> mSysAllowlistedApps = new ArraySet<>(); private final ArraySet<String> mDefaultActiveApps = new ArraySet<>(); - public PowerWhitelistBackend(Context context) { + public PowerAllowlistBackend(Context context) { this(context, IDeviceIdleController.Stub.asInterface( ServiceManager.getService(DEVICE_IDLE_SERVICE))); } @VisibleForTesting - PowerWhitelistBackend(Context context, IDeviceIdleController deviceIdleService) { + PowerAllowlistBackend(Context context, IDeviceIdleController deviceIdleService) { mAppContext = context.getApplicationContext(); mDeviceIdleService = deviceIdleService; refreshList(); } - public int getWhitelistSize() { - return mWhitelistedApps.size(); + public int getAllowlistSize() { + return mAllowlistedApps.size(); } - public boolean isSysWhitelisted(String pkg) { - return mSysWhitelistedApps.contains(pkg); + /** + * Check if target package is in System allow list + */ + public boolean isSysAllowlisted(String pkg) { + return mSysAllowlistedApps.contains(pkg); } - public boolean isWhitelisted(String pkg) { - if (mWhitelistedApps.contains(pkg)) { + /** + * Check if target package is in allow list + */ + public boolean isAllowlisted(String pkg) { + if (mAllowlistedApps.contains(pkg)) { return true; } @@ -87,7 +93,7 @@ public class PowerWhitelistBackend { */ public boolean isDefaultActiveApp(String pkg) { // Additionally, check if pkg is default dialer/sms. They are considered essential apps and - // should be automatically whitelisted (otherwise user may be able to set restriction on + // should be automatically allowlisted (otherwise user may be able to set restriction on // them, leading to bad device behavior.) if (mDefaultActiveApps.contains(pkg)) { @@ -103,12 +109,17 @@ public class PowerWhitelistBackend { return false; } - public boolean isWhitelisted(String[] pkgs) { + /** + * + * @param pkgs a list of packageName + * @return true when one of package is in allow list + */ + public boolean isAllowlisted(String[] pkgs) { if (ArrayUtils.isEmpty(pkgs)) { return false; } for (String pkg : pkgs) { - if (isWhitelisted(pkg)) { + if (isAllowlisted(pkg)) { return true; } } @@ -116,40 +127,51 @@ public class PowerWhitelistBackend { return false; } + /** + * Add app into power save allow list. + * @param pkg packageName + */ public void addApp(String pkg) { try { mDeviceIdleService.addPowerSaveWhitelistApp(pkg); - mWhitelistedApps.add(pkg); + mAllowlistedApps.add(pkg); } catch (RemoteException e) { Log.w(TAG, "Unable to reach IDeviceIdleController", e); } } + /** + * Remove package from power save allow list. + * @param pkg + */ public void removeApp(String pkg) { try { mDeviceIdleService.removePowerSaveWhitelistApp(pkg); - mWhitelistedApps.remove(pkg); + mAllowlistedApps.remove(pkg); } catch (RemoteException e) { Log.w(TAG, "Unable to reach IDeviceIdleController", e); } } + /** + * Refresh all of lists + */ @VisibleForTesting public void refreshList() { - mSysWhitelistedApps.clear(); - mWhitelistedApps.clear(); + mSysAllowlistedApps.clear(); + mAllowlistedApps.clear(); mDefaultActiveApps.clear(); if (mDeviceIdleService == null) { return; } try { - final String[] whitelistedApps = mDeviceIdleService.getFullPowerWhitelist(); - for (String app : whitelistedApps) { - mWhitelistedApps.add(app); + final String[] allowlistedApps = mDeviceIdleService.getFullPowerWhitelist(); + for (String app : allowlistedApps) { + mAllowlistedApps.add(app); } - final String[] sysWhitelistedApps = mDeviceIdleService.getSystemPowerWhitelist(); - for (String app : sysWhitelistedApps) { - mSysWhitelistedApps.add(app); + final String[] sysAllowlistedApps = mDeviceIdleService.getSystemPowerWhitelist(); + for (String app : sysAllowlistedApps) { + mSysAllowlistedApps.add(app); } final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEPHONY); @@ -171,9 +193,13 @@ public class PowerWhitelistBackend { } } - public static PowerWhitelistBackend getInstance(Context context) { + /** + * @param context + * @return a PowerAllowlistBackend object + */ + public static PowerAllowlistBackend getInstance(Context context) { if (sInstance == null) { - sInstance = new PowerWhitelistBackend(context); + sInstance = new PowerAllowlistBackend(context); } return sInstance; } diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java index 4941f7e42bf6..8ac434957cd9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java @@ -135,7 +135,7 @@ public class AppRestrictionsHelper { // Ignore } } else { - // Blacklist all other apps, system or downloaded + // Denylist all other apps, system or downloaded try { ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId); if (info != null) { @@ -258,11 +258,11 @@ public class AppRestrictionsHelper { } } - // Establish master/slave relationship for entries that share a package name + // Establish primary/secondary relationship for entries that share a package name HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>(); for (SelectableAppInfo info : mVisibleApps) { if (packageMap.containsKey(info.packageName)) { - info.masterEntry = packageMap.get(info.packageName); + info.primaryEntry = packageMap.get(info.packageName); } else { packageMap.put(info.packageName, info); } @@ -366,12 +366,12 @@ public class AppRestrictionsHelper { public CharSequence appName; public CharSequence activityName; public Drawable icon; - public SelectableAppInfo masterEntry; + public SelectableAppInfo primaryEntry; @Override public String toString() { return packageName + ": appName=" + appName + "; activityName=" + activityName - + "; icon=" + icon + "; masterEntry=" + masterEntry; + + "; icon=" + icon + "; primaryEntry=" + primaryEntry; } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java index 11c799ea9df5..94e28f2b5e22 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java @@ -290,12 +290,12 @@ public class RestrictedLockUtilsTest { @Test public void sendShowAdminSupportDetailsIntent_extraRestrictionProvided() { EnforcedAdmin enforcedAdmin = new EnforcedAdmin(); - enforcedAdmin.enforcedRestriction = "Dummy"; + enforcedAdmin.enforcedRestriction = "Fake"; RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, enforcedAdmin); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext).startActivityAsUser(intentCaptor.capture(), any()); - assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Dummy"); + assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Fake"); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java index 69d0f2e71c17..9e4cde866ef2 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java @@ -23,16 +23,16 @@ import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) -public class MasterSwitchControllerTest { +public class PrimarySwitchControllerTest { @Rule public final ExpectedException thrown = ExpectedException.none(); - private MasterSwitchController mController; + private PrimarySwitchController mController; @Before public void setUp() { - mController = new TestMasterSwitchController("123"); + mController = new TestPrimarySwitchController("123"); } @Test @@ -49,11 +49,11 @@ public class MasterSwitchControllerTest { mController.getBundle(); } - static class TestMasterSwitchController extends MasterSwitchController { + static class TestPrimarySwitchController extends PrimarySwitchController { private String mKey; - TestMasterSwitchController(String key) { + TestPrimarySwitchController(String key) { mKey = key; } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java index a740e683642a..bd0100b67a94 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java @@ -35,7 +35,7 @@ import android.content.Context; import android.content.pm.ProviderInfo; import android.os.Bundle; -import com.android.settingslib.drawer.MasterSwitchControllerTest.TestMasterSwitchController; +import com.android.settingslib.drawer.PrimarySwitchControllerTest.TestPrimarySwitchController; import com.android.settingslib.drawer.SwitchController.MetaData; import org.junit.Before; @@ -124,8 +124,8 @@ public class SwitchesProviderTest { } @Test - public void getSwitchData_shouldNotReturnMasterSwitchData() { - final SwitchController controller = new TestMasterSwitchController("123"); + public void getSwitchData_shouldNotReturnPrimarySwitchData() { + final SwitchController controller = new TestPrimarySwitchController("123"); mSwitchesProvider.addSwitchController(controller); mSwitchesProvider.attachInfo(mContext, mProviderInfo); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java index 20908925feff..4f11fb1f782f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java @@ -47,7 +47,7 @@ import org.robolectric.shadows.ShadowPackageManager; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class}) -public class PowerWhitelistBackendTest { +public class PowerAllowlistBackendTest { private static final String PACKAGE_ONE = "com.example.packageone"; private static final String PACKAGE_TWO = "com.example.packagetwo"; @@ -56,7 +56,7 @@ public class PowerWhitelistBackendTest { private IDeviceIdleController mDeviceIdleService; @Mock private DevicePolicyManager mDevicePolicyManager; - private PowerWhitelistBackend mPowerWhitelistBackend; + private PowerAllowlistBackend mPowerAllowlistBackend; private ShadowPackageManager mPackageManager; private Context mContext; @@ -74,81 +74,81 @@ public class PowerWhitelistBackendTest { mPackageManager.setSystemFeature(PackageManager.FEATURE_TELEPHONY, true); doReturn(mDevicePolicyManager).when(mContext).getSystemService(DevicePolicyManager.class); - mPowerWhitelistBackend = new PowerWhitelistBackend(mContext, mDeviceIdleService); + mPowerAllowlistBackend = new PowerAllowlistBackend(mContext, mDeviceIdleService); } @Test - public void testIsWhitelisted() throws Exception { + public void testIsAllowlisted() throws Exception { doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getFullPowerWhitelist(); - mPowerWhitelistBackend.refreshList(); + mPowerAllowlistBackend.refreshList(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse(); - assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_ONE})).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_TWO})).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse(); - mPowerWhitelistBackend.addApp(PACKAGE_TWO); + mPowerAllowlistBackend.addApp(PACKAGE_TWO); verify(mDeviceIdleService, atLeastOnce()).addPowerSaveWhitelistApp(PACKAGE_TWO); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted( + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted( new String[] {PACKAGE_ONE, PACKAGE_TWO})).isTrue(); - mPowerWhitelistBackend.removeApp(PACKAGE_TWO); + mPowerAllowlistBackend.removeApp(PACKAGE_TWO); verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_TWO); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse(); - assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_ONE})).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_TWO})).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse(); - mPowerWhitelistBackend.removeApp(PACKAGE_ONE); + mPowerAllowlistBackend.removeApp(PACKAGE_ONE); verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_ONE); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse(); - assertThat(mPowerWhitelistBackend.isWhitelisted( + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted( new String[] {PACKAGE_ONE, PACKAGE_TWO})).isFalse(); } @Test - public void isWhitelisted_shouldWhitelistDefaultSms() { + public void isAllowlisted_shouldAllowlistDefaultSms() { final String testSms = "com.android.test.defaultsms"; ShadowSmsApplication.setDefaultSmsApplication(new ComponentName(testSms, "receiver")); - mPowerWhitelistBackend.refreshList(); + mPowerAllowlistBackend.refreshList(); - assertThat(mPowerWhitelistBackend.isWhitelisted(testSms)).isTrue(); - assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testSms)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(testSms)).isTrue(); + assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testSms)).isTrue(); } @Test - public void isWhitelisted_shouldWhitelistDefaultDialer() { + public void isAllowlisted_shouldAllowlistDefaultDialer() { final String testDialer = "com.android.test.defaultdialer"; ShadowDefaultDialerManager.setDefaultDialerApplication(testDialer); - mPowerWhitelistBackend.refreshList(); + mPowerAllowlistBackend.refreshList(); - assertThat(mPowerWhitelistBackend.isWhitelisted(testDialer)).isTrue(); - assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testDialer)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(testDialer)).isTrue(); + assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testDialer)).isTrue(); } @Test - public void isWhitelisted_shouldWhitelistActiveDeviceAdminApp() { + public void isAllowlisted_shouldAllowlistActiveDeviceAdminApp() { doReturn(true).when(mDevicePolicyManager).packageHasActiveAdmins(PACKAGE_ONE); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue(); - assertThat(mPowerWhitelistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue(); } @Test - public void testIsSystemWhitelisted() throws Exception { + public void testIsSystemAllowlisted() throws Exception { doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getSystemPowerWhitelist(); - mPowerWhitelistBackend.refreshList(); + mPowerAllowlistBackend.refreshList(); - assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_ONE)).isTrue(); - assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_TWO)).isFalse(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse(); + assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_TWO)).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse(); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java index b930aa6ee1bd..84d722ad16df 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java @@ -197,61 +197,61 @@ public class InputMethodAndSubtypeUtilCompatTest { public void isValidSystemNonAuxAsciiCapableIme() { // System IME w/ no subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, false))) + createFakeIme(true, false))) .isFalse(); // System IME w/ non-Aux and non-ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, false, createDummySubtype("keyboard", false, false)))) + createFakeIme(true, false, createFakeSubtype("keyboard", false, false)))) .isFalse(); // System IME w/ non-Aux and ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, false, createDummySubtype("keyboard", false, true)))) + createFakeIme(true, false, createFakeSubtype("keyboard", false, true)))) .isTrue(); // System IME w/ Aux and ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, true, createDummySubtype("keyboard", true, true)))) + createFakeIme(true, true, createFakeSubtype("keyboard", true, true)))) .isFalse(); // System IME w/ non-Aux and ASCII-capable "voice" subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, false, createDummySubtype("voice", false, true)))) + createFakeIme(true, false, createFakeSubtype("voice", false, true)))) .isFalse(); // System IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, false, - createDummySubtype("keyboard", false, true), - createDummySubtype("keyboard", false, false)))) + createFakeIme(true, false, + createFakeSubtype("keyboard", false, true), + createFakeSubtype("keyboard", false, false)))) .isTrue(); // Non-system IME w/ non-Aux and ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(false, false, createDummySubtype("keyboard", false, true)))) + createFakeIme(false, false, createFakeSubtype("keyboard", false, true)))) .isFalse(); } - private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme, + private static InputMethodInfo createFakeIme(boolean isSystem, boolean isAuxIme, InputMethodSubtype... subtypes) { final ResolveInfo ri = new ResolveInfo(); final ServiceInfo si = new ServiceInfo(); final ApplicationInfo ai = new ApplicationInfo(); - ai.packageName = "com.example.android.dummyime"; + ai.packageName = "com.example.android.fakeime"; ai.enabled = true; ai.flags |= (isSystem ? ApplicationInfo.FLAG_SYSTEM : 0); si.applicationInfo = ai; si.enabled = true; - si.packageName = "com.example.android.dummyime"; - si.name = "Dummy IME"; + si.packageName = "com.example.android.fakeime"; + si.name = "Fake IME"; si.exported = true; - si.nonLocalizedLabel = "Dummy IME"; + si.nonLocalizedLabel = "Fake IME"; ri.serviceInfo = si; return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false); } - private static InputMethodSubtype createDummySubtype( + private static InputMethodSubtype createFakeSubtype( String mode, boolean isAuxiliary, boolean isAsciiCapable) { return new InputMethodSubtypeBuilder() .setSubtypeNameResId(0) diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java index 5171dda9bff7..97d87051402e 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java @@ -195,55 +195,55 @@ public class InputMethodAndSubtypeUtilTest { public void isValidNonAuxAsciiCapableIme() { // IME w/ no subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(false))) + createFakeIme(false))) .isFalse(); // IME w/ non-Aux and non-ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(false, createDummySubtype("keyboard", false, false)))) + createFakeIme(false, createFakeSubtype("keyboard", false, false)))) .isFalse(); // IME w/ non-Aux and ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(false, createDummySubtype("keyboard", false, true)))) + createFakeIme(false, createFakeSubtype("keyboard", false, true)))) .isTrue(); // IME w/ Aux and ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(true, createDummySubtype("keyboard", true, true)))) + createFakeIme(true, createFakeSubtype("keyboard", true, true)))) .isFalse(); // IME w/ non-Aux and ASCII-capable "voice" subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(false, createDummySubtype("voice", false, true)))) + createFakeIme(false, createFakeSubtype("voice", false, true)))) .isFalse(); // IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(false, - createDummySubtype("keyboard", false, true), - createDummySubtype("keyboard", false, false)))) + createFakeIme(false, + createFakeSubtype("keyboard", false, true), + createFakeSubtype("keyboard", false, false)))) .isTrue(); } - private static InputMethodInfo createDummyIme(boolean isAuxIme, + private static InputMethodInfo createFakeIme(boolean isAuxIme, InputMethodSubtype... subtypes) { final ResolveInfo ri = new ResolveInfo(); final ServiceInfo si = new ServiceInfo(); final ApplicationInfo ai = new ApplicationInfo(); - ai.packageName = "com.example.android.dummyime"; + ai.packageName = "com.example.android.fakeime"; ai.enabled = true; si.applicationInfo = ai; si.enabled = true; - si.packageName = "com.example.android.dummyime"; - si.name = "Dummy IME"; + si.packageName = "com.example.android.fakeime"; + si.name = "Fake IME"; si.exported = true; - si.nonLocalizedLabel = "Dummy IME"; + si.nonLocalizedLabel = "Fake IME"; ri.serviceInfo = si; return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false); } - private static InputMethodSubtype createDummySubtype( + private static InputMethodSubtype createFakeSubtype( String mode, boolean isAuxiliary, boolean isAsciiCapable) { return new InputMethodSubtypeBuilder() .setSubtypeNameResId(0) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index c1543fd91060..bfd5b1ccbc25 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -81,6 +81,7 @@ public class SettingsHelper { sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_START_TIME); sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME); sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); + sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); } private interface SettingsLookup { diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index aa960875ec6f..319b44ce216f 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -320,19 +320,6 @@ <!-- Permissions required for CTS test - AdbManagerTest --> <uses-permission android:name="android.permission.MANAGE_DEBUGGING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp --> - <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases --> - <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <uses-permission android:name="android.car.permission.CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <uses-permission android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <uses-permission android:name="android.car.permission.CONTROL_APP_BLOCKING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <uses-permission android:name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" /> - <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp index 176035f73b65..1c680bb9d25e 100644 --- a/packages/SimAppDialog/Android.bp +++ b/packages/SimAppDialog/Android.bp @@ -7,7 +7,8 @@ android_app { static_libs: [ "androidx.legacy_legacy-support-v4", - "setup-wizard-lib", + "setupcompat", + "setupdesign", ], resource_dirs: ["res"], diff --git a/packages/SimAppDialog/AndroidManifest.xml b/packages/SimAppDialog/AndroidManifest.xml index 873f6c5bac54..e7368f35ed5a 100644 --- a/packages/SimAppDialog/AndroidManifest.xml +++ b/packages/SimAppDialog/AndroidManifest.xml @@ -23,7 +23,7 @@ android:name=".InstallCarrierAppActivity" android:exported="true" android:permission="android.permission.NETWORK_SETTINGS" - android:theme="@style/SuwThemeGlif.Light"> + android:theme="@style/SudThemeGlif.Light"> </activity> </application> </manifest> diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml index 12f9bb6b13ea..68113dbf5df0 100644 --- a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml +++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml @@ -14,18 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.setupwizardlib.GlifLayout +<com.google.android.setupdesign.GlifLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/setup_wizard_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:icon="@drawable/ic_signal_cellular_alt_rounded" - app:suwHeaderText="@string/install_carrier_app_title" - app:suwFooter="@layout/install_carrier_app_footer"> + app:sucHeaderText="@string/install_carrier_app_title"> <LinearLayout - style="@style/SuwContentFrame" + style="@style/SudContentFrame" android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -33,12 +32,12 @@ <TextView android:id="@+id/install_carrier_app_description" - style="@style/SuwDescription.Glif" + style="@style/SudDescription.Glif" android:text="@string/install_carrier_app_description_default" android:layout_width="match_parent" android:layout_height="wrap_content"/> - <com.android.setupwizardlib.view.FillContentLayout + <com.google.android.setupdesign.view.FillContentLayout android:id="@+id/illo_container" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -47,12 +46,12 @@ <ImageView android:src="@drawable/illo_sim_app_dialog" - style="@style/SuwContentIllustration" + style="@style/SudContentIllustration" android:contentDescription="@string/install_carrier_app_image_content_description" android:layout_width="match_parent" android:layout_height="match_parent"/> - </com.android.setupwizardlib.view.FillContentLayout> -</LinearLayout> + </com.google.android.setupdesign.view.FillContentLayout> + </LinearLayout> -</com.android.setupwizardlib.GlifLayout> +</com.google.android.setupdesign.GlifLayout> diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml deleted file mode 100644 index 10dcb77a6584..000000000000 --- a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> - -<com.android.setupwizardlib.view.ButtonBarLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/footer" - style="@style/SuwGlifButtonBar.Stackable" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <Button - android:id="@+id/skip_button" - style="@style/SuwGlifButton.Secondary" - android:text="@string/install_carrier_app_defer_action" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1"/> - - <Button - android:id="@+id/download_button" - style="@style/SuwGlifButton.Primary" - android:text="@string/install_carrier_app_download_action" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> -</com.android.setupwizardlib.view.ButtonBarLayout> diff --git a/packages/SimAppDialog/res/values/styles.xml b/packages/SimAppDialog/res/values/styles.xml new file mode 100644 index 000000000000..824e3802aca1 --- /dev/null +++ b/packages/SimAppDialog/res/values/styles.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 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. +--> +<resources> + + <style name="SetupWizardPartnerResource"> + <!-- Disable to use partner overlay theme for outside setupwizard flow. --> + <item name="sucUsePartnerResource">false</item> + <!-- Enable heavy theme style inside setupwizard flow. --> + <item name="sudUsePartnerHeavyTheme">true</item> + </style> + +</resources> diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java index abe82a885a94..0b6f9bb4f9e0 100644 --- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java +++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java @@ -17,14 +17,17 @@ package com.android.simappdialog; import android.app.Activity; import android.content.Intent; +import android.content.res.Resources; import android.os.Bundle; import android.sysprop.SetupWizardProperties; import android.text.TextUtils; import android.view.View; -import android.widget.Button; import android.widget.TextView; -import com.android.setupwizardlib.util.WizardManagerHelper; +import com.google.android.setupcompat.template.FooterBarMixin; +import com.google.android.setupcompat.template.FooterButton; +import com.google.android.setupdesign.GlifLayout; +import com.google.android.setupdesign.util.ThemeResolver; /** * Activity that gives a user the choice to download the SIM app or defer until a later time @@ -35,7 +38,7 @@ import com.android.setupwizardlib.util.WizardManagerHelper; * Can display the carrier app name if its passed into the intent with key * {@link #BUNDLE_KEY_CARRIER_NAME} */ -public class InstallCarrierAppActivity extends Activity implements View.OnClickListener { +public class InstallCarrierAppActivity extends Activity { /** * Key for the carrier app name that will be displayed as the app to download. If unset, a * default description will be used @@ -50,20 +53,33 @@ public class InstallCarrierAppActivity extends Activity implements View.OnClickL protected void onCreate(Bundle icicle) { // Setup theme for aosp/pixel setTheme( - WizardManagerHelper.getThemeRes( - SetupWizardProperties.theme().orElse(""), - R.style.SuwThemeGlif_Light - ) - ); + new ThemeResolver.Builder() + .setDefaultTheme(R.style.SudThemeGlifV3_Light) + .build() + .resolve(SetupWizardProperties.theme().orElse(""), + /* suppressDayNight= */ false)); super.onCreate(icicle); setContentView(R.layout.install_carrier_app_activity); - Button notNowButton = findViewById(R.id.skip_button); - notNowButton.setOnClickListener(this); + GlifLayout layout = findViewById(R.id.setup_wizard_layout); + FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class); + mixin.setSecondaryButton( + new FooterButton.Builder(this) + .setText(R.string.install_carrier_app_defer_action) + .setListener(this::onSkipButtonClick) + .setButtonType(FooterButton.ButtonType.SKIP) + .setTheme(R.style.SudGlifButton_Secondary) + .build()); + + mixin.setPrimaryButton( + new FooterButton.Builder(this) + .setText(R.string.install_carrier_app_download_action) + .setListener(this::onDownloadButtonClick) + .setButtonType(FooterButton.ButtonType.OTHER) + .setTheme(R.style.SudGlifButton_Primary) + .build()); - Button downloadButton = findViewById(R.id.download_button); - downloadButton.setOnClickListener(this); // Show/hide illo depending on whether one was provided in a resource overlay boolean showIllo = getResources().getBoolean(R.bool.show_sim_app_dialog_illo); @@ -82,15 +98,17 @@ public class InstallCarrierAppActivity extends Activity implements View.OnClickL } @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.skip_button: - finish(DEFER_RESULT); - break; - case R.id.download_button: - finish(DOWNLOAD_RESULT); - break; - } + protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) { + theme.applyStyle(R.style.SetupWizardPartnerResource, true); + super.onApplyThemeResource(theme, resid, first); + } + + protected void onSkipButtonClick(View view) { + finish(DEFER_RESULT); + } + + protected void onDownloadButtonClick(View view) { + finish(DOWNLOAD_RESULT); } private void finish(int resultCode) { diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml index 3c641afea0d6..ed870f8bb2ef 100644 --- a/packages/SystemUI/res/layout/media_view.xml +++ b/packages/SystemUI/res/layout/media_view.xml @@ -210,8 +210,98 @@ android:layout_width="@dimen/qs_media_icon_size" android:layout_height="@dimen/qs_media_icon_size" /> - <!-- Buttons to remove this view when no longer needed --> - <include - layout="@layout/qs_media_panel_options" - android:visibility="gone" /> + <!-- Constraints are set here as they are the same regardless of host --> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/qs_media_panel_outer_padding" + android:layout_marginStart="@dimen/qs_media_panel_outer_padding" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:id="@+id/media_text" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="@color/media_primary_text" + android:text="@string/controls_media_title" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintBottom_toTopOf="@id/remove_text" + app:layout_constraintVertical_chainStyle="spread_inside"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/qs_media_panel_outer_padding" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:id="@+id/remove_text" + android:fontFamily="@*android:string/config_headlineFontFamily" + android:singleLine="true" + android:textColor="@color/media_primary_text" + android:text="@string/controls_media_close_session" + app:layout_constraintTop_toBottomOf="@id/media_text" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintBottom_toTopOf="@id/settings"/> + + <FrameLayout + android:id="@+id/settings" + android:background="@drawable/qs_media_light_source" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/qs_media_panel_outer_padding" + android:paddingBottom="@dimen/qs_media_panel_outer_padding" + android:minWidth="48dp" + android:minHeight="48dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/remove_text"> + + <TextView + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="@android:color/white" + android:text="@string/controls_media_settings_button" /> + </FrameLayout> + + <FrameLayout + android:id="@+id/cancel" + android:background="@drawable/qs_media_light_source" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:paddingBottom="@dimen/qs_media_panel_outer_padding" + android:minWidth="48dp" + android:minHeight="48dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/dismiss" > + + <TextView + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="@android:color/white" + android:text="@string/cancel" /> + </FrameLayout> + + <FrameLayout + android:id="@+id/dismiss" + android:background="@drawable/qs_media_light_source" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:paddingBottom="@dimen/qs_media_panel_outer_padding" + android:minWidth="48dp" + android:minHeight="48dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent"> + + <TextView + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="@android:color/white" + android:text="@string/controls_media_dismiss_button" + /> + </FrameLayout> </com.android.systemui.util.animation.TransitionLayout> diff --git a/packages/SystemUI/res/layout/qs_media_panel_options.xml b/packages/SystemUI/res/layout/qs_media_panel_options.xml deleted file mode 100644 index e72c0e85fb26..000000000000 --- a/packages/SystemUI/res/layout/qs_media_panel_options.xml +++ /dev/null @@ -1,61 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2019 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 - --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/qs_media_controls_options" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center" - android:padding="16dp" - android:orientation="vertical"> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="48dp" - android:layout_weight="1" - android:minWidth="48dp" - android:layout_gravity="start|bottom" - android:gravity="bottom" - android:id="@+id/remove" - android:orientation="horizontal"> - <ImageView - android:layout_width="18dp" - android:layout_height="18dp" - android:id="@+id/remove_icon" - android:layout_marginEnd="16dp" - android:tint="@color/media_primary_text" - android:src="@drawable/ic_clear"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/remove_text" - android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - android:singleLine="true" - android:textColor="@color/media_primary_text" - android:text="@string/controls_media_close_session" /> - </LinearLayout> - <TextView - android:id="@+id/cancel" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:layout_weight="1" - android:minWidth="48dp" - android:layout_gravity="end|bottom" - android:gravity="bottom" - android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - android:textColor="@android:color/white" - android:text="@string/cancel" /> -</LinearLayout> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index fa620df12b87..fba43a628387 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -515,13 +515,11 @@ <!-- Whether or not to add a "people" notifications section --> <bool name="config_usePeopleFiltering">false</bool> - <!-- Defines the blacklist for system icons. That is to say, the icons in the status bar that - are part of the blacklist are never displayed. Each item in the blacklist must be a string - defined in core/res/res/config.xml to properly blacklist the icon. - - TODO: See if we can rename this config variable. + <!-- Defines system icons to be excluded from the display. That is to say, the icons in the + status bar that are part of this list are never displayed. Each item in the list must be a + string defined in core/res/res/config.xml to properly exclude the icon. --> - <string-array name="config_statusBarIconBlackList" translatable="false"> + <string-array name="config_statusBarIconsToExclude" translatable="false"> <item>@*android:string/status_bar_rotate</item> <item>@*android:string/status_bar_headset</item> </string-array> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 77d3f4513957..823c1ff2fdd0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2799,7 +2799,7 @@ <!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=NONE] --> <string name="controls_media_close_session">Hide the current session.</string> <!-- Label for a button that will hide media controls [CHAR_LIMIT=30] --> - <string name="controls_media_dismiss_button">Hide</string> + <string name="controls_media_dismiss_button">Dismiss</string> <!-- Label for button to resume media playback [CHAR_LIMIT=NONE] --> <string name="controls_media_resume">Resume</string> <!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] --> diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 4df66602bb7e..6512624f5064 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -281,9 +281,11 @@ public class AppOpsControllerImpl implements AppOpsController, * @return {@code true} iff the app-op for should be shown to the user */ private boolean isUserVisible(int appOpCode, int uid, String packageName) { - // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission - // which may be user senstive, so for now always show it to the user. - if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) { + // currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION + // does not correspond to a platform permission + // which may be user sensitive, so for now always show it to the user. + if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW + || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index bb572191fe3c..c5639ea6fcde 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -84,6 +84,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; +import com.android.systemui.bubbles.animation.StackAnimationController; import com.android.systemui.bubbles.dagger.BubbleModule; import com.android.systemui.dump.DumpManager; import com.android.systemui.model.SysUiState; @@ -173,6 +174,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Nullable private BubbleStackView mStackView; private BubbleIconFactory mBubbleIconFactory; + /** + * The relative position of the stack when we removed it and nulled it out. If the stack is + * re-created, it will re-appear at this position. + */ + @Nullable private BubbleStackView.RelativeStackPosition mPositionFromRemovedStack; + // Tracks the id of the current (foreground) user. private int mCurrentUserId; // Saves notification keys of active bubbles when users are switched. @@ -736,6 +743,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator, mSysUiState, this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged, this::hideCurrentInputMethod); + mStackView.setStackStartPosition(mPositionFromRemovedStack); mStackView.addView(mBubbleScrim); if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); @@ -806,6 +814,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi try { mAddedToWindowManager = false; if (mStackView != null) { + mPositionFromRemovedStack = mStackView.getRelativeStackPosition(); mWindowManager.removeView(mStackView); mStackView.removeView(mBubbleScrim); mStackView = null; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index d0f61817d1cc..fa0d2ba84b4e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -310,7 +310,6 @@ public class BubbleExpandedView extends LinearLayout { // Set ActivityView's alpha value as zero, since there is no view content to be shown. setContentVisibility(false); - mActivityViewContainer.setBackgroundColor(Color.WHITE); mActivityViewContainer.setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { @@ -439,9 +438,11 @@ public class BubbleExpandedView extends LinearLayout { } void applyThemeAttrs() { - final TypedArray ta = mContext.obtainStyledAttributes( - new int[] {android.R.attr.dialogCornerRadius}); + final TypedArray ta = mContext.obtainStyledAttributes(new int[] { + android.R.attr.dialogCornerRadius, + android.R.attr.colorBackgroundFloating}); mCornerRadius = ta.getDimensionPixelSize(0, 0); + mActivityViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE)); ta.recycle(); if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows( diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index b3fbfd6a0f30..ec9644af7013 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -253,13 +253,8 @@ public class BubbleStackView extends FrameLayout /** Layout change listener that moves the stack to the nearest valid position on rotation. */ private OnLayoutChangeListener mOrientationChangedListener; - /** Whether the stack was on the left side of the screen prior to rotation. */ - private boolean mWasOnLeftBeforeRotation = false; - /** - * How far down the screen the stack was before rotation, in terms of percentage of the way down - * the allowable region. Defaults to -1 if not set. - */ - private float mVerticalPosPercentBeforeRotation = -1; + + @Nullable private RelativeStackPosition mRelativeStackPositionBeforeRotation; private int mMaxBubbles; private int mBubbleSize; @@ -940,9 +935,10 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setTranslationY(getExpandedViewY()); mExpandedViewContainer.setAlpha(1f); } - if (mVerticalPosPercentBeforeRotation >= 0) { - mStackAnimationController.moveStackToSimilarPositionAfterRotation( - mWasOnLeftBeforeRotation, mVerticalPosPercentBeforeRotation); + if (mRelativeStackPositionBeforeRotation != null) { + mStackAnimationController.setStackPosition( + mRelativeStackPositionBeforeRotation); + mRelativeStackPositionBeforeRotation = null; } removeOnLayoutChangeListener(mOrientationChangedListener); }; @@ -1189,13 +1185,7 @@ public class BubbleStackView extends FrameLayout com.android.internal.R.dimen.status_bar_height); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); - final RectF allowablePos = mStackAnimationController.getAllowableStackPositionRegion(); - mWasOnLeftBeforeRotation = mStackAnimationController.isStackOnLeftSide(); - mVerticalPosPercentBeforeRotation = - (mStackAnimationController.getStackPosition().y - allowablePos.top) - / (allowablePos.bottom - allowablePos.top); - mVerticalPosPercentBeforeRotation = - Math.max(0f, Math.min(1f, mVerticalPosPercentBeforeRotation)); + mRelativeStackPositionBeforeRotation = mStackAnimationController.getRelativeStackPosition(); addOnLayoutChangeListener(mOrientationChangedListener); hideFlyoutImmediate(); @@ -1459,7 +1449,7 @@ public class BubbleStackView extends FrameLayout if (getBubbleCount() == 0 && mShouldShowUserEducation) { // Override the default stack position if we're showing user education. mStackAnimationController.setStackPosition( - mStackAnimationController.getDefaultStartPosition()); + mStackAnimationController.getStartPosition()); } if (getBubbleCount() == 0) { @@ -1674,7 +1664,7 @@ public class BubbleStackView extends FrameLayout // Post so we have height of mUserEducationView mUserEducationView.post(() -> { final int viewHeight = mUserEducationView.getHeight(); - PointF stackPosition = mStackAnimationController.getDefaultStartPosition(); + PointF stackPosition = mStackAnimationController.getStartPosition(); final float translationY = stackPosition.y + (mBubbleSize / 2) - (viewHeight / 2); mUserEducationView.setTranslationY(translationY); mUserEducationView.animate() @@ -2740,10 +2730,18 @@ public class BubbleStackView extends FrameLayout .floatValue(); } + public void setStackStartPosition(RelativeStackPosition position) { + mStackAnimationController.setStackStartPosition(position); + } + public PointF getStackPosition() { return mStackAnimationController.getStackPosition(); } + public RelativeStackPosition getRelativeStackPosition() { + return mStackAnimationController.getRelativeStackPosition(); + } + /** * Logs the bubble UI event. * @@ -2797,4 +2795,47 @@ public class BubbleStackView extends FrameLayout } return bubbles; } + + /** + * Representation of stack position that uses relative properties rather than absolute + * coordinates. This is used to maintain similar stack positions across configuration changes. + */ + public static class RelativeStackPosition { + /** Whether to place the stack at the leftmost allowed position. */ + private boolean mOnLeft; + + /** + * How far down the vertically allowed region to place the stack. For example, if the stack + * allowed region is between y = 100 and y = 1100 and this is 0.2f, we'll place the stack at + * 100 + (0.2f * 1000) = 300. + */ + private float mVerticalOffsetPercent; + + public RelativeStackPosition(boolean onLeft, float verticalOffsetPercent) { + mOnLeft = onLeft; + mVerticalOffsetPercent = clampVerticalOffsetPercent(verticalOffsetPercent); + } + + /** Constructs a relative position given a region and a point in that region. */ + public RelativeStackPosition(PointF position, RectF region) { + mOnLeft = position.x < region.width() / 2; + mVerticalOffsetPercent = + clampVerticalOffsetPercent((position.y - region.top) / region.height()); + } + + /** Ensures that the offset percent is between 0f and 1f. */ + private float clampVerticalOffsetPercent(float offsetPercent) { + return Math.max(0f, Math.min(1f, offsetPercent)); + } + + /** + * Given an allowable stack position region, returns the point within that region + * represented by this relative position. + */ + public PointF getAbsolutePositionInRegion(RectF region) { + return new PointF( + mOnLeft ? region.left : region.right, + region.top + mVerticalOffsetPercent * region.height()); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index b378469c4c98..e835ea206e59 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -35,6 +35,7 @@ import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.systemui.R; +import com.android.systemui.bubbles.BubbleStackView; import com.android.systemui.util.FloatingContentCoordinator; import com.android.systemui.util.animation.PhysicsAnimator; import com.android.systemui.util.magnetictarget.MagnetizedObject; @@ -125,6 +126,9 @@ public class StackAnimationController extends */ private Rect mAnimatingToBounds = new Rect(); + /** Initial starting location for the stack. */ + @Nullable private BubbleStackView.RelativeStackPosition mStackStartPosition; + /** Whether or not the stack's start position has been set. */ private boolean mStackMovedToStartPosition = false; @@ -431,21 +435,6 @@ public class StackAnimationController extends return stackPos; } - /** - * Moves the stack in response to rotation. We keep it in the most similar position by keeping - * it on the same side, and positioning it the same percentage of the way down the screen - * (taking status bar/nav bar into account by using the allowable region's height). - */ - public void moveStackToSimilarPositionAfterRotation(boolean wasOnLeft, float verticalPercent) { - final RectF allowablePos = getAllowableStackPositionRegion(); - final float allowableRegionHeight = allowablePos.bottom - allowablePos.top; - - final float x = wasOnLeft ? allowablePos.left : allowablePos.right; - final float y = (allowableRegionHeight * verticalPercent) + allowablePos.top; - - setStackPosition(new PointF(x, y)); - } - /** Description of current animation controller state. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("StackAnimationController state:"); @@ -815,7 +804,7 @@ public class StackAnimationController extends } else { // When all children are removed ensure stack position is sane setStackPosition(mRestingStackPosition == null - ? getDefaultStartPosition() + ? getStartPosition() : mRestingStackPosition); // Remove the stack from the coordinator since we don't have any bubbles and aren't @@ -868,7 +857,7 @@ public class StackAnimationController extends mLayout.setVisibility(View.INVISIBLE); mLayout.post(() -> { setStackPosition(mRestingStackPosition == null - ? getDefaultStartPosition() + ? getStartPosition() : mRestingStackPosition); mStackMovedToStartPosition = true; mLayout.setVisibility(View.VISIBLE); @@ -938,15 +927,47 @@ public class StackAnimationController extends } } - /** Returns the default stack position, which is on the top left. */ - public PointF getDefaultStartPosition() { - boolean isRtl = mLayout != null - && mLayout.getResources().getConfiguration().getLayoutDirection() - == View.LAYOUT_DIRECTION_RTL; - return new PointF(isRtl - ? getAllowableStackPositionRegion().right - : getAllowableStackPositionRegion().left, - getAllowableStackPositionRegion().top + mStackStartingVerticalOffset); + public void setStackPosition(BubbleStackView.RelativeStackPosition position) { + setStackPosition(position.getAbsolutePositionInRegion(getAllowableStackPositionRegion())); + } + + public BubbleStackView.RelativeStackPosition getRelativeStackPosition() { + return new BubbleStackView.RelativeStackPosition( + mStackPosition, getAllowableStackPositionRegion()); + } + + /** + * Sets the starting position for the stack, where it will be located when the first bubble is + * added. + */ + public void setStackStartPosition(BubbleStackView.RelativeStackPosition position) { + mStackStartPosition = position; + } + + /** + * Returns the starting stack position. If {@link #setStackStartPosition} was called, this will + * return that position - otherwise, a reasonable default will be returned. + */ + @Nullable public PointF getStartPosition() { + if (mLayout == null) { + return null; + } + + if (mStackStartPosition == null) { + // Start on the left if we're in LTR, right otherwise. + final boolean startOnLeft = + mLayout.getResources().getConfiguration().getLayoutDirection() + != View.LAYOUT_DIRECTION_RTL; + + final float startingVerticalOffset = mLayout.getResources().getDimensionPixelOffset( + R.dimen.bubble_stack_starting_offset_y); + + mStackStartPosition = new BubbleStackView.RelativeStackPosition( + startOnLeft, + startingVerticalOffset / getAllowableStackPositionRegion().height()); + } + + return mStackStartPosition.getAbsolutePositionInRegion(getAllowableStackPositionRegion()); } private boolean isStackPositionSet() { diff --git a/packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt b/packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt new file mode 100644 index 000000000000..cca0f1653757 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 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.systemui.controls + +import android.content.ComponentName +import android.graphics.drawable.Icon +import androidx.annotation.GuardedBy +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Icon cache for custom icons sent with controls. + * + * It assumes that only one component can be current at the time, to minimize the number of icons + * stored at a given time. + */ +@Singleton +class CustomIconCache @Inject constructor() { + + private var currentComponent: ComponentName? = null + @GuardedBy("cache") + private val cache: MutableMap<String, Icon> = LinkedHashMap() + + /** + * Store an icon in the cache. + * + * If the icons currently stored do not correspond to the component to be stored, the cache is + * cleared first. + */ + fun store(component: ComponentName, controlId: String, icon: Icon?) { + if (component != currentComponent) { + clear() + currentComponent = component + } + synchronized(cache) { + if (icon != null) { + cache.put(controlId, icon) + } else { + cache.remove(controlId) + } + } + } + + /** + * Retrieves a custom icon stored in the cache. + * + * It will return null if the component requested is not the one whose icons are stored, or if + * there is no icon cached for that id. + */ + fun retrieve(component: ComponentName, controlId: String): Icon? { + if (component != currentComponent) return null + return synchronized(cache) { + cache.get(controlId) + } + } + + private fun clear() { + synchronized(cache) { + cache.clear() + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt index ff40a8a883ae..f68388d5db3f 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt @@ -29,6 +29,7 @@ import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.controls.CustomIconCache import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.StructureInfo import com.android.systemui.globalactions.GlobalActionsComponent @@ -42,7 +43,8 @@ import javax.inject.Inject class ControlsEditingActivity @Inject constructor( private val controller: ControlsControllerImpl, broadcastDispatcher: BroadcastDispatcher, - private val globalActionsComponent: GlobalActionsComponent + private val globalActionsComponent: GlobalActionsComponent, + private val customIconCache: CustomIconCache ) : LifecycleActivity() { companion object { @@ -170,7 +172,7 @@ class ControlsEditingActivity @Inject constructor( private fun setUpList() { val controls = controller.getFavoritesForStructure(component, structure) - model = FavoritesModel(component, controls, favoritesModelCallback) + model = FavoritesModel(customIconCache, component, controls, favoritesModelCallback) val elevation = resources.getFloat(R.dimen.control_card_elevation) val recyclerView = requireViewById<RecyclerView>(R.id.list) recyclerView.alpha = 0.0f diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt index 4ef64a5cddbf..ad0e7a541f98 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt @@ -114,11 +114,27 @@ data class ControlStatusWrapper( val controlStatus: ControlStatus ) : ElementWrapper(), ControlInterface by controlStatus +private fun nullIconGetter(_a: ComponentName, _b: String): Icon? = null + data class ControlInfoWrapper( override val component: ComponentName, val controlInfo: ControlInfo, override var favorite: Boolean ) : ElementWrapper(), ControlInterface { + + var customIconGetter: (ComponentName, String) -> Icon? = ::nullIconGetter + private set + + // Separate constructor so the getter is not used in auto-generated methods + constructor( + component: ComponentName, + controlInfo: ControlInfo, + favorite: Boolean, + customIconGetter: (ComponentName, String) -> Icon? + ): this(component, controlInfo, favorite) { + this.customIconGetter = customIconGetter + } + override val controlId: String get() = controlInfo.controlId override val title: CharSequence @@ -128,8 +144,7 @@ data class ControlInfoWrapper( override val deviceType: Int get() = controlInfo.deviceType override val customIcon: Icon? - // Will need to address to support for edit activity - get() = null + get() = customIconGetter(component, controlId) } data class DividerWrapper( diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt index 524250134e9b..f9ce6362f4f8 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt @@ -21,6 +21,7 @@ import android.util.Log import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.android.systemui.controls.ControlInterface +import com.android.systemui.controls.CustomIconCache import com.android.systemui.controls.controller.ControlInfo import java.util.Collections @@ -35,6 +36,7 @@ import java.util.Collections * @property favoritesModelCallback callback to notify on first change and empty favorites */ class FavoritesModel( + private val customIconCache: CustomIconCache, private val componentName: ComponentName, favorites: List<ControlInfo>, private val favoritesModelCallback: FavoritesModelCallback @@ -83,7 +85,7 @@ class FavoritesModel( } override val elements: List<ElementWrapper> = favorites.map { - ControlInfoWrapper(componentName, it, true) + ControlInfoWrapper(componentName, it, true, customIconCache::retrieve) } + DividerWrapper() /** diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index 22d6b6bb75c3..e15380b42a78 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -92,7 +92,7 @@ class ControlActionCoordinatorImpl @Inject constructor( override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) { bouncerOrRun(Action(cvh.cws.ci.controlId, { cvh.action(FloatAction(templateId, newValue)) - }, true /* blockable */)) + }, false /* blockable */)) } override fun longPress(cvh: ControlViewHolder) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 1eb7e2168a6a..5f75c96be128 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -44,6 +44,7 @@ import android.widget.Space import android.widget.TextView import com.android.systemui.R import com.android.systemui.controls.ControlsServiceInfo +import com.android.systemui.controls.CustomIconCache import com.android.systemui.controls.controller.ControlInfo import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.controller.StructureInfo @@ -75,7 +76,8 @@ class ControlsUiControllerImpl @Inject constructor ( @Main val sharedPreferences: SharedPreferences, val controlActionCoordinator: ControlActionCoordinator, private val activityStarter: ActivityStarter, - private val shadeController: ShadeController + private val shadeController: ShadeController, + private val iconCache: CustomIconCache ) : ControlsUiController { companion object { @@ -502,6 +504,7 @@ class ControlsUiControllerImpl @Inject constructor ( controls.forEach { c -> controlsById.get(ControlKey(componentName, c.getControlId()))?.let { Log.d(ControlsUiController.TAG, "onRefreshState() for id: " + c.getControlId()) + iconCache.store(componentName, c.controlId, c.customIcon) val cws = ControlWithState(componentName, it.ci, c) val key = ControlKey(componentName, c.getControlId()) controlsById.put(key, cws) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index de5316885b94..7f610d1a8467 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -3,7 +3,6 @@ package com.android.systemui.media import android.content.Context import android.content.Intent import android.content.res.Configuration -import android.graphics.Color import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS import android.util.Log import android.util.MathUtils @@ -151,7 +150,7 @@ class MediaCarouselController @Inject constructor( pageIndicator = mediaFrame.requireViewById(R.id.media_page_indicator) mediaCarouselScrollHandler = MediaCarouselScrollHandler(mediaCarousel, pageIndicator, executor, mediaManager::onSwipeToDismiss, this::updatePageIndicatorLocation, - falsingManager) + this::closeGuts, falsingManager) isRtl = context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL inflateSettingsButton() mediaContent = mediaCarousel.requireViewById(R.id.media_carousel) @@ -470,6 +469,12 @@ class MediaCarouselController @Inject constructor( } } + fun closeGuts() { + mediaPlayers.values.forEach { + it.closeGuts(true) + } + } + /** * Update the size of the carousel, remeasuring it if necessary. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt index 3096908aca21..77cac5023db3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt @@ -56,6 +56,7 @@ class MediaCarouselScrollHandler( private val mainExecutor: DelayableExecutor, private val dismissCallback: () -> Unit, private var translationChangedListener: () -> Unit, + private val closeGuts: () -> Unit, private val falsingManager: FalsingManager ) { /** @@ -452,6 +453,7 @@ class MediaCarouselScrollHandler( val nowScrolledIn = scrollIntoCurrentMedia != 0 if (newIndex != activeMediaIndex || wasScrolledIn != nowScrolledIn) { activeMediaIndex = newIndex + closeGuts() updatePlayerVisibilities() } val relativeLocation = activeMediaIndex.toFloat() + if (playerWidthPlusPadding > 0) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 3fc162ead6d1..e55678dc986b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -16,6 +16,8 @@ package com.android.systemui.media; +import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS; + import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -45,6 +47,7 @@ import com.android.settingslib.widget.AdaptiveIcon; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.util.animation.TransitionLayout; import java.util.List; @@ -52,6 +55,8 @@ import java.util.concurrent.Executor; import javax.inject.Inject; +import dagger.Lazy; + /** * A view controller used for Media Playback. */ @@ -59,6 +64,8 @@ public class MediaControlPanel { private static final String TAG = "MediaControlPanel"; private static final float DISABLED_ALPHA = 0.38f; + private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS); + // Button IDs for QS controls static final int[] ACTION_IDS = { R.id.action0, @@ -78,6 +85,8 @@ public class MediaControlPanel { private MediaViewController mMediaViewController; private MediaSession.Token mToken; private MediaController mController; + private KeyguardDismissUtil mKeyguardDismissUtil; + private Lazy<MediaDataManager> mMediaDataManagerLazy; private int mBackgroundColor; private int mAlbumArtSize; private int mAlbumArtRadius; @@ -93,12 +102,15 @@ public class MediaControlPanel { @Inject public MediaControlPanel(Context context, @Background Executor backgroundExecutor, ActivityStarter activityStarter, MediaViewController mediaViewController, - SeekBarViewModel seekBarViewModel) { + SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager, + KeyguardDismissUtil keyguardDismissUtil) { mContext = context; mBackgroundExecutor = backgroundExecutor; mActivityStarter = activityStarter; mSeekBarViewModel = seekBarViewModel; mMediaViewController = mediaViewController; + mMediaDataManagerLazy = lazyMediaDataManager; + mKeyguardDismissUtil = keyguardDismissUtil; loadDimens(); mViewOutlineProvider = new ViewOutlineProvider() { @@ -174,6 +186,21 @@ public class MediaControlPanel { mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver); mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar()); mMediaViewController.attach(player); + + mViewHolder.getPlayer().setOnLongClickListener(v -> { + if (!mMediaViewController.isGutsVisible()) { + mMediaViewController.openGuts(); + return true; + } else { + return false; + } + }); + mViewHolder.getCancel().setOnClickListener(v -> { + closeGuts(); + }); + mViewHolder.getSettings().setOnClickListener(v -> { + mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */); + }); } /** @@ -205,6 +232,7 @@ public class MediaControlPanel { PendingIntent clickIntent = data.getClickIntent(); if (clickIntent != null) { mViewHolder.getPlayer().setOnClickListener(v -> { + if (mMediaViewController.isGutsVisible()) return; mActivityStarter.postStartActivityDismissingKeyguard(clickIntent); }); } @@ -329,14 +357,38 @@ public class MediaControlPanel { final MediaController controller = getController(); mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller)); - // Set up long press menu - // TODO: b/156036025 bring back media guts + // Dismiss + mViewHolder.getDismiss().setOnClickListener(v -> { + if (data.getNotificationKey() != null) { + closeGuts(); + mKeyguardDismissUtil.executeWhenUnlocked(() -> { + mMediaDataManagerLazy.get().dismissMediaData(data.getNotificationKey(), + MediaViewController.GUTS_ANIMATION_DURATION + 100); + return true; + }, /* requiresShadeOpen */ true); + } else { + Log.w(TAG, "Dismiss media with null notification. Token uid=" + + data.getToken().getUid()); + } + }); // TODO: We don't need to refresh this state constantly, only if the state actually changed // to something which might impact the measurement mMediaViewController.refreshState(); } + /** + * Close the guts for this player. + * @param immediate {@code true} if it should be closed without animation + */ + public void closeGuts(boolean immediate) { + mMediaViewController.closeGuts(immediate); + } + + private void closeGuts() { + closeGuts(false); + } + @UiThread private Drawable scaleDrawable(Icon icon) { if (icon == null) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index d82150f2346b..8a51c8515852 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -48,6 +48,7 @@ import com.android.systemui.statusbar.notification.MediaNotificationProcessor import com.android.systemui.statusbar.notification.row.HybridGroupManager import com.android.systemui.util.Assert import com.android.systemui.util.Utils +import com.android.systemui.util.concurrency.DelayableExecutor import java.io.FileDescriptor import java.io.IOException import java.io.PrintWriter @@ -90,7 +91,7 @@ fun isMediaNotification(sbn: StatusBarNotification): Boolean { class MediaDataManager( private val context: Context, @Background private val backgroundExecutor: Executor, - @Main private val foregroundExecutor: Executor, + @Main private val foregroundExecutor: DelayableExecutor, private val mediaControllerFactory: MediaControllerFactory, private val broadcastDispatcher: BroadcastDispatcher, dumpManager: DumpManager, @@ -107,7 +108,7 @@ class MediaDataManager( constructor( context: Context, @Background backgroundExecutor: Executor, - @Main foregroundExecutor: Executor, + @Main foregroundExecutor: DelayableExecutor, mediaControllerFactory: MediaControllerFactory, dumpManager: DumpManager, broadcastDispatcher: BroadcastDispatcher, @@ -183,10 +184,7 @@ class MediaDataManager( val listenersCopy = listeners.toSet() val toRemove = mediaEntries.filter { it.value.packageName == packageName } toRemove.forEach { - mediaEntries.remove(it.key) - listenersCopy.forEach { listener -> - listener.onMediaDataRemoved(it.key) - } + removeEntry(it.key, listenersCopy) } } @@ -269,6 +267,18 @@ class MediaDataManager( } } + private fun removeEntry(key: String, listenersCopy: Set<Listener>) { + mediaEntries.remove(key) + listenersCopy.forEach { + it.onMediaDataRemoved(key) + } + } + + fun dismissMediaData(key: String, delay: Long) { + val listenersCopy = listeners.toSet() + foregroundExecutor.executeDelayed({ removeEntry(key, listenersCopy) }, delay) + } + private fun loadMediaDataInBgForResumption( userId: Int, desc: MediaDescription, diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index fc33391a9ad1..70f01d576a9c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -293,6 +293,13 @@ class MediaHierarchyManager @Inject constructor( return viewHost } + /** + * Close the guts in all players in [MediaCarouselController]. + */ + fun closeGuts() { + mediaCarouselController.closeGuts() + } + private fun createUniqueObjectHost(): UniqueObjectHostView { val viewHost = UniqueObjectHostView(context) viewHost.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt index 38817d7b579e..92eeed46388d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt @@ -37,6 +37,11 @@ class MediaViewController @Inject constructor( private val mediaHostStatesManager: MediaHostStatesManager ) { + companion object { + @JvmField + val GUTS_ANIMATION_DURATION = 500L + } + /** * A listener when the current dimensions of the player change */ @@ -169,6 +174,12 @@ class MediaViewController @Inject constructor( */ val expandedLayout = ConstraintSet() + /** + * Whether the guts are visible for the associated player. + */ + var isGutsVisible = false + private set + init { collapsedLayout.load(context, R.xml.media_collapsed) expandedLayout.load(context, R.xml.media_expanded) @@ -189,6 +200,37 @@ class MediaViewController @Inject constructor( configurationController.removeCallback(configurationListener) } + /** + * Show guts with an animated transition. + */ + fun openGuts() { + if (isGutsVisible) return + isGutsVisible = true + animatePendingStateChange(GUTS_ANIMATION_DURATION, 0L) + setCurrentState(currentStartLocation, + currentEndLocation, + currentTransitionProgress, + applyImmediately = false) + } + + /** + * Close the guts for the associated player. + * + * @param immediate if `false`, it will animate the transition. + */ + @JvmOverloads + fun closeGuts(immediate: Boolean = false) { + if (!isGutsVisible) return + isGutsVisible = false + if (!immediate) { + animatePendingStateChange(GUTS_ANIMATION_DURATION, 0L) + } + setCurrentState(currentStartLocation, + currentEndLocation, + currentTransitionProgress, + applyImmediately = immediate) + } + private fun ensureAllMeasurements() { val mediaStates = mediaHostStatesManager.mediaHostStates for (entry in mediaStates) { @@ -203,6 +245,24 @@ class MediaViewController @Inject constructor( if (expansion > 0) expandedLayout else collapsedLayout /** + * Set the views to be showing/hidden based on the [isGutsVisible] for a given + * [TransitionViewState]. + */ + private fun setGutsViewState(viewState: TransitionViewState) { + PlayerViewHolder.controlsIds.forEach { id -> + viewState.widgetStates.get(id)?.let { state -> + // Make sure to use the unmodified state if guts are not visible + state.alpha = if (isGutsVisible) 0f else state.alpha + state.gone = if (isGutsVisible) true else state.gone + } + } + PlayerViewHolder.gutsIds.forEach { id -> + viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f + viewState.widgetStates.get(id)?.gone = !isGutsVisible + } + } + + /** * Obtain a new viewState for a given media state. This usually returns a cached state, but if * it's not available, it will recreate one by measuring, which may be expensive. */ @@ -211,7 +271,7 @@ class MediaViewController @Inject constructor( return null } // Only a subset of the state is relevant to get a valid viewState. Let's get the cachekey - var cacheKey = getKey(state, tmpKey) + var cacheKey = getKey(state, isGutsVisible, tmpKey) val viewState = viewStates[cacheKey] if (viewState != null) { // we already have cached this measurement, let's continue @@ -228,6 +288,7 @@ class MediaViewController @Inject constructor( constraintSetForExpansion(state.expansion), TransitionViewState()) + setGutsViewState(result) // We don't want to cache interpolated or null states as this could quickly fill up // our cache. We only cache the start and the end states since the interpolation // is cheap @@ -252,11 +313,12 @@ class MediaViewController @Inject constructor( return result } - private fun getKey(state: MediaHostState, result: CacheKey): CacheKey { + private fun getKey(state: MediaHostState, guts: Boolean, result: CacheKey): CacheKey { result.apply { heightMeasureSpec = state.measurementInput?.heightMeasureSpec ?: 0 widthMeasureSpec = state.measurementInput?.widthMeasureSpec ?: 0 expansion = state.expansion + gutsVisible = guts } return result } @@ -432,5 +494,6 @@ class MediaViewController @Inject constructor( private data class CacheKey( var widthMeasureSpec: Int = -1, var heightMeasureSpec: Int = -1, - var expansion: Float = 0.0f + var expansion: Float = 0.0f, + var gutsVisible: Boolean = false ) diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt index 600fdc27ef89..11551aca80f2 100644 --- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt @@ -23,6 +23,7 @@ import android.widget.ImageButton import android.widget.ImageView import android.widget.SeekBar import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintSet import com.android.systemui.R import com.android.systemui.util.animation.TransitionLayout @@ -59,6 +60,11 @@ class PlayerViewHolder private constructor(itemView: View) { val action3 = itemView.requireViewById<ImageButton>(R.id.action3) val action4 = itemView.requireViewById<ImageButton>(R.id.action4) + // Settings screen + val cancel = itemView.requireViewById<View>(R.id.cancel) + val dismiss = itemView.requireViewById<View>(R.id.dismiss) + val settings = itemView.requireViewById<View>(R.id.settings) + init { (player.background as IlluminationDrawable).let { it.registerLightSource(seamless) @@ -67,6 +73,9 @@ class PlayerViewHolder private constructor(itemView: View) { it.registerLightSource(action2) it.registerLightSource(action3) it.registerLightSource(action4) + it.registerLightSource(cancel) + it.registerLightSource(dismiss) + it.registerLightSource(settings) } } @@ -83,9 +92,6 @@ class PlayerViewHolder private constructor(itemView: View) { } } - // Settings screen - val options = itemView.requireViewById<View>(R.id.qs_media_controls_options) - companion object { /** * Creates a PlayerViewHolder. @@ -105,5 +111,29 @@ class PlayerViewHolder private constructor(itemView: View) { progressTimes.layoutDirection = View.LAYOUT_DIRECTION_LTR } } + + val controlsIds = setOf( + R.id.icon, + R.id.app_name, + R.id.album_art, + R.id.header_title, + R.id.header_artist, + R.id.media_seamless, + R.id.notification_media_progress_time, + R.id.media_progress_bar, + R.id.action0, + R.id.action1, + R.id.action2, + R.id.action3, + R.id.action4, + R.id.icon + ) + val gutsIds = setOf( + R.id.media_text, + R.id.remove_text, + R.id.cancel, + R.id.dismiss, + R.id.settings + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java index 8a67da53e6a2..b28730d004f6 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java @@ -16,11 +16,13 @@ package com.android.systemui.onehanded; +import android.content.ContentResolver; import android.content.Context; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; +import android.provider.Settings; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -48,12 +50,15 @@ import javax.inject.Singleton; @Singleton public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Dumpable { private static final String TAG = "OneHandedTutorialHandler"; + private static final int MAX_TUTORIAL_SHOW_COUNT = 2; private final Rect mLastUpdatedBounds = new Rect(); private final WindowManager mWindowManager; private View mTutorialView; private Point mDisplaySize = new Point(); private Handler mUpdateHandler; + private ContentResolver mContentResolver; + private boolean mCanShowTutorial; /** * Container of the tutorial panel showing at outside region when one handed starting @@ -71,6 +76,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du @Inject public OneHandedTutorialHandler(Context context) { context.getDisplay().getRealSize(mDisplaySize); + mContentResolver = context.getContentResolver(); mUpdateHandler = new Handler(); mWindowManager = context.getSystemService(WindowManager.class); mTargetViewContainer = new FrameLayout(context); @@ -79,12 +85,20 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du R.fraction.config_one_handed_offset, 1, 1)); mTutorialView = LayoutInflater.from(context).inflate(R.xml.one_handed_tutorial, null); mTargetViewContainer.addView(mTutorialView); - createOrUpdateTutorialTarget(); + mCanShowTutorial = (Settings.Secure.getInt(mContentResolver, + Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT) + ? false : true; + if (mCanShowTutorial) { + createOrUpdateTutorialTarget(); + } } @Override public void onStartFinished(Rect bounds) { - mUpdateHandler.post(() -> updateFinished(View.VISIBLE, 0f)); + mUpdateHandler.post(() -> { + updateFinished(View.VISIBLE, 0f); + updateTutorialCount(); + }); } @Override @@ -94,10 +108,23 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du } private void updateFinished(int visible, float finalPosition) { + if (!canShowTutorial()) { + return; + } + mTargetViewContainer.setVisibility(visible); mTargetViewContainer.setTranslationY(finalPosition); } + private void updateTutorialCount() { + int showCount = Settings.Secure.getInt(mContentResolver, + Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0); + showCount = Math.min(MAX_TUTORIAL_SHOW_COUNT, showCount + 1); + mCanShowTutorial = showCount < MAX_TUTORIAL_SHOW_COUNT; + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, showCount); + } + /** * Adds the tutorial target view to the WindowManager and update its layout, so it's ready * to be animated in. @@ -153,7 +180,19 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du pw.println(mLastUpdatedBounds); } + private boolean canShowTutorial() { + if (!mCanShowTutorial) { + mTargetViewContainer.setVisibility(View.GONE); + return false; + } + + return true; + } + private void onAnimationUpdate(float value) { + if (!canShowTutorial()) { + return; + } mTargetViewContainer.setVisibility(View.VISIBLE); mTargetViewContainer.setTransitionGroup(true); mTargetViewContainer.setTranslationY(value - mTargetViewContainer.getHeight()); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 7c3743bd8bcf..025341cf4fba 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -99,6 +99,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements private final Handler mUpdateHandler; private final PipBoundsHandler mPipBoundsHandler; private final PipAnimationController mPipAnimationController; + private final PipUiEventLogger mPipUiEventLoggerLogger; private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); private final Rect mLastReportedBounds = new Rect(); private final int mEnterExitAnimationDuration; @@ -209,7 +210,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @Nullable Divider divider, @NonNull DisplayController displayController, - @NonNull PipAnimationController pipAnimationController) { + @NonNull PipAnimationController pipAnimationController, + @NonNull PipUiEventLogger pipUiEventLogger) { mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsHandler = boundsHandler; @@ -217,6 +219,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements .getInteger(R.integer.config_pipResizeAnimationDuration); mSurfaceTransactionHelper = surfaceTransactionHelper; mPipAnimationController = pipAnimationController; + mPipUiEventLoggerLogger = pipUiEventLogger; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSplitDivider = divider; displayController.addDisplayWindowListener(this); @@ -279,6 +282,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements return; } + mPipUiEventLoggerLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); final Configuration initialConfig = mInitialState.remove(mToken.asBinder()); final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation() != mPipBoundsHandler.getDisplayRotation(); @@ -381,6 +386,9 @@ public class PipTaskOrganizer extends TaskOrganizer implements mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); mPictureInPictureParams = mTaskInfo.pictureInPictureParams; + mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo); + mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER); + if (mShouldDeferEnteringPip) { if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing"); // if deferred, hide the surface till fixed rotation is completed @@ -454,10 +462,11 @@ public class PipTaskOrganizer extends TaskOrganizer implements private void sendOnPipTransitionStarted( @PipAnimationController.TransitionDirection int direction) { + final Rect pipBounds = new Rect(mLastReportedBounds); runOnMainHandler(() -> { for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); - callback.onPipTransitionStarted(mTaskInfo.baseActivity, direction); + callback.onPipTransitionStarted(mTaskInfo.baseActivity, direction, pipBounds); } }); } @@ -513,6 +522,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements mPictureInPictureParams = null; mInPip = false; mExitingPip = false; + mPipUiEventLoggerLogger.setTaskInfo(null); } @Override @@ -628,7 +638,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements * {@link PictureInPictureParams} would affect the bounds. */ private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) { - final boolean changed = (mPictureInPictureParams == null) ? true : !Objects.equals( + final boolean changed = (mPictureInPictureParams == null) || !Objects.equals( mPictureInPictureParams.getAspectRatioRational(), params.getAspectRatioRational()); if (changed) { mPictureInPictureParams = params; @@ -973,7 +983,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements /** * Callback when the pip transition is started. */ - void onPipTransitionStarted(ComponentName activity, int direction); + void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds); /** * Callback when the pip transition is finished. diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java new file mode 100644 index 000000000000..5e2cd9c111b2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 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.systemui.pip; + +import android.app.TaskInfo; + +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; + +import javax.inject.Inject; +import javax.inject.Singleton; + + +/** + * Helper class that ends PiP log to UiEvent, see also go/uievent + */ +@Singleton +public class PipUiEventLogger { + + private final UiEventLogger mUiEventLogger; + + private TaskInfo mTaskInfo; + + @Inject + public PipUiEventLogger(UiEventLogger uiEventLogger) { + mUiEventLogger = uiEventLogger; + } + + public void setTaskInfo(TaskInfo taskInfo) { + mTaskInfo = taskInfo; + } + + /** + * Sends log via UiEvent, reference go/uievent for how to debug locally + */ + public void log(PipUiEventEnum event) { + if (mTaskInfo == null) { + return; + } + mUiEventLogger.log(event, mTaskInfo.userId, mTaskInfo.topActivity.getPackageName()); + } + + /** + * Enums for logging the PiP events to UiEvent + */ + public enum PipUiEventEnum implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "Activity enters picture-in-picture mode") + PICTURE_IN_PICTURE_ENTER(603), + + @UiEvent(doc = "Expands from picture-in-picture to fullscreen") + PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN(604), + + @UiEvent(doc = "Removes picture-in-picture by tap close button") + PICTURE_IN_PICTURE_TAP_TO_REMOVE(605), + + @UiEvent(doc = "Removes picture-in-picture by drag to dismiss area") + PICTURE_IN_PICTURE_DRAG_TO_REMOVE(606), + + @UiEvent(doc = "Shows picture-in-picture menu") + PICTURE_IN_PICTURE_SHOW_MENU(607), + + @UiEvent(doc = "Hides picture-in-picture menu") + PICTURE_IN_PICTURE_HIDE_MENU(608), + + @UiEvent(doc = "Changes the aspect ratio of picture-in-picture window. This is inherited" + + " from previous Tron-based logging and currently not in use.") + PICTURE_IN_PICTURE_CHANGE_ASPECT_RATIO(609), + + @UiEvent(doc = "User resize of the picture-in-picture window") + PICTURE_IN_PICTURE_RESIZE(610); + + private final int mId; + + PipUiEventEnum(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 6e75253d8604..9dfa864e2ee7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -47,6 +47,7 @@ import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -259,7 +260,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio PipSnapAlgorithm pipSnapAlgorithm, PipTaskOrganizer pipTaskOrganizer, SysUiState sysUiState, - ConfigurationController configController) { + ConfigurationController configController, + PipUiEventLogger pipUiEventLogger) { mContext = context; mActivityManager = ActivityManager.getService(); @@ -280,7 +282,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mMediaController, mInputConsumerController); mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, - floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState); + floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState, + pipUiEventLogger); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, mTouchHandler.getMotionHelper()); displayController.addDisplayChangingController(mRotationController); @@ -373,10 +376,10 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio } @Override - public void onPipTransitionStarted(ComponentName activity, int direction) { + public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) { if (isOutPipDirection(direction)) { // Exiting PIP, save the reentry bounds to restore to when re-entering. - updateReentryBounds(); + updateReentryBounds(pipBounds); mPipBoundsHandler.onSaveReentryBounds(activity, mReentryBounds); } // Disable touches while the animation is running @@ -393,15 +396,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio /** * Update the bounds used to save the re-entry size and snap fraction when exiting PIP. */ - public void updateReentryBounds() { - // On phones, the expansion animation that happens on pip tap before restoring - // to fullscreen makes it so that the last reported bounds are the expanded - // bounds. We want to restore to the unexpanded bounds when re-entering pip, - // so we use the bounds before expansion (normal) instead of the reported - // bounds. - Rect reentryBounds = mTouchHandler.getNormalBounds(); - // Apply the snap fraction of the current bounds to the normal bounds. - final Rect bounds = mPipTaskOrganizer.getLastReportedBounds(); + public void updateReentryBounds(Rect bounds) { + final Rect reentryBounds = mTouchHandler.getUserResizeBounds(); float snapFraction = mPipBoundsHandler.getSnapFraction(bounds); mPipBoundsHandler.applySnapFraction(reentryBounds, snapFraction); mReentryBounds.set(reentryBounds); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index ee8f295e0c30..19138fdba788 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -154,7 +154,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private final PipTaskOrganizer.PipTransitionCallback mPipTransitionCallback = new PipTaskOrganizer.PipTransitionCallback() { @Override - public void onPipTransitionStarted(ComponentName activity, int direction) {} + public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {} @Override public void onPipTransitionFinished(ComponentName activity, int direction) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index d884fa956edc..9c42f8bff378 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -52,6 +52,7 @@ import com.android.internal.policy.TaskResizingAlgorithm; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.util.DeviceConfigProxy; import com.android.wm.shell.R; @@ -88,6 +89,7 @@ public class PipResizeGestureHandler { private final Point mMaxSize = new Point(); private final Point mMinSize = new Point(); private final Rect mLastResizeBounds = new Rect(); + private final Rect mUserResizeBounds = new Rect(); private final Rect mLastDownBounds = new Rect(); private final Rect mDragCornerSize = new Rect(); private final Rect mTmpTopLeftCorner = new Rect(); @@ -109,13 +111,15 @@ public class PipResizeGestureHandler { private InputMonitor mInputMonitor; private InputEventReceiver mInputEventReceiver; private PipTaskOrganizer mPipTaskOrganizer; + private PipUiEventLogger mPipUiEventLogger; private int mCtrlType; public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler, PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig, PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier, - Runnable updateMovementBoundsRunnable, SysUiState sysUiState) { + Runnable updateMovementBoundsRunnable, SysUiState sysUiState, + PipUiEventLogger pipUiEventLogger) { mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); @@ -125,6 +129,7 @@ public class PipResizeGestureHandler { mMovementBoundsSupplier = movementBoundsSupplier; mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; mSysUiState = sysUiState; + mPipUiEventLogger = pipUiEventLogger; context.getDisplay().getRealSize(mMaxSize); reloadResources(); @@ -181,6 +186,7 @@ public class PipResizeGestureHandler { void onActivityUnpinned() { mIsAttached = false; + mUserResizeBounds.setEmpty(); updateIsEnabled(); } @@ -329,6 +335,7 @@ public class PipResizeGestureHandler { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (!mLastResizeBounds.isEmpty()) { + mUserResizeBounds.set(mLastResizeBounds); mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, (Rect bounds) -> { new Handler(Looper.getMainLooper()).post(() -> { @@ -337,6 +344,8 @@ public class PipResizeGestureHandler { resetState(); }); }); + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE); } else { resetState(); } @@ -351,6 +360,14 @@ public class PipResizeGestureHandler { mThresholdCrossed = false; } + void setUserResizeBounds(Rect bounds) { + mUserResizeBounds.set(bounds); + } + + Rect getUserResizeBounds() { + return mUserResizeBounds; + } + void updateMaxSize(int maxX, int maxY) { mMaxSize.set(maxX, maxY); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 7fc2823fc248..b20ea4e5c836 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -34,7 +34,6 @@ import android.graphics.drawable.TransitionDrawable; import android.os.Handler; import android.os.RemoteException; import android.util.Log; -import android.util.Pair; import android.util.Size; import android.view.Gravity; import android.view.IPinnedStackController; @@ -55,13 +54,13 @@ import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.systemui.R; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DismissCircleView; @@ -94,6 +93,8 @@ public class PipTouchHandler { private final WindowManager mWindowManager; private final IActivityManager mActivityManager; private final PipBoundsHandler mPipBoundsHandler; + private final PipUiEventLogger mPipUiEventLogger; + private PipResizeGestureHandler mPipResizeGestureHandler; private IPinnedStackController mPinnedStackController; @@ -132,9 +133,6 @@ public class PipTouchHandler { // The current movement bounds private Rect mMovementBounds = new Rect(); - // The current resized bounds, changed by user resize. - // This is used during expand/un-expand to save/restore the user's resized size. - @VisibleForTesting Rect mResizedBounds = new Rect(); // The reference inset bounds, used to determine the dismiss fraction private Rect mInsetBounds = new Rect(); @@ -198,11 +196,7 @@ public class PipTouchHandler { @Override public void onPipDismiss() { - Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext, - mActivityManager); - if (topPipActivity.first != null) { - MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext, topPipActivity); - } + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE); mTouchState.removeDoubleTapTimeoutCallback(); mMotionHelper.dismissPip(); } @@ -223,7 +217,8 @@ public class PipTouchHandler { FloatingContentCoordinator floatingContentCoordinator, DeviceConfigProxy deviceConfig, PipSnapAlgorithm pipSnapAlgorithm, - SysUiState sysUiState) { + SysUiState sysUiState, + PipUiEventLogger pipUiEventLogger) { // Initialize the Pip input consumer mContext = context; mActivityManager = activityManager; @@ -238,7 +233,7 @@ public class PipTouchHandler { mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, deviceConfig, pipTaskOrganizer, this::getMovementBounds, - this::updateMovementBounds, sysUiState); + this::updateMovementBounds, sysUiState, pipUiEventLogger); mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(), true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()), @@ -259,6 +254,8 @@ public class PipTouchHandler { pipTaskOrganizer, pipSnapAlgorithm, this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler); + mPipUiEventLogger = pipUiEventLogger; + mTargetView = new DismissCircleView(context); mTargetViewContainer = new FrameLayout(context); mTargetViewContainer.setBackgroundDrawable( @@ -307,11 +304,8 @@ public class PipTouchHandler { hideDismissTarget(); }); - Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext, - mActivityManager); - if (topPipActivity.first != null) { - MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, topPipActivity); - } + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE); } }); @@ -383,7 +377,6 @@ public class PipTouchHandler { mFloatingContentCoordinator.onContentRemoved(mMotionHelper); } - mResizedBounds.setEmpty(); mPipResizeGestureHandler.onActivityUnpinned(); } @@ -393,9 +386,8 @@ public class PipTouchHandler { mMotionHelper.synchronizePinnedStackBounds(); updateMovementBounds(); if (direction == TRANSITION_DIRECTION_TO_PIP) { - // updates mResizedBounds only if it's an entering PiP animation - // mResized should be otherwise updated in setMenuState. - mResizedBounds.set(mMotionHelper.getBounds()); + // Set the initial bounds as the user resize bounds. + mPipResizeGestureHandler.setUserResizeBounds(mMotionHelper.getBounds()); } if (mShowPipMenuOnAnimationEnd) { @@ -808,9 +800,7 @@ public class PipTouchHandler { // Save the current snap fraction and if we do not drag or move the PiP, then // we store back to this snap fraction. Otherwise, we'll reset the snap // fraction and snap to the closest edge. - // Also save the current resized bounds so when the menu disappears, we can restore it. if (resize) { - mResizedBounds.set(mMotionHelper.getBounds()); Rect expandedBounds = new Rect(mExpandedBounds); mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds, mMovementBounds, mExpandedMovementBounds, callback); @@ -839,7 +829,7 @@ public class PipTouchHandler { } if (mDeferResizeToNormalBoundsUntilRotation == -1) { - Rect restoreBounds = new Rect(mResizedBounds); + Rect restoreBounds = new Rect(getUserResizeBounds()); Rect restoredMovementBounds = new Rect(); mSnapAlgorithm.getMovementBounds(restoreBounds, mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); @@ -856,8 +846,10 @@ public class PipTouchHandler { // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip // as well, or it can't handle a11y focus and pip menu can't perform any action. onRegistrationChanged(menuState == MENU_STATE_NONE); - if (menuState != MENU_STATE_CLOSE) { - MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL); + if (menuState == MENU_STATE_NONE) { + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_HIDE_MENU); + } else if (menuState == MENU_STATE_FULL) { + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_SHOW_MENU); } } @@ -890,6 +882,10 @@ public class PipTouchHandler { return mNormalBounds; } + Rect getUserResizeBounds() { + return mPipResizeGestureHandler.getUserResizeBounds(); + } + /** * Gesture controlling normal movement of the PIP. */ diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 2138f092b790..01670285c0f4 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -663,7 +663,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio }; @Override - public void onPipTransitionStarted(ComponentName activity, int direction) { } + public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) { } @Override public void onPipTransitionFinished(ComponentName activity, int direction) { diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index d5a14f7bef2f..affc5ee1fdf0 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -45,7 +45,6 @@ import javax.inject.Singleton @Singleton class PrivacyItemController @Inject constructor( - context: Context, private val appOpsController: AppOpsController, @Main uiExecutor: DelayableExecutor, @Background private val bgExecutor: Executor, @@ -57,16 +56,21 @@ class PrivacyItemController @Inject constructor( @VisibleForTesting internal companion object { - val OPS = intArrayOf(AppOpsManager.OP_CAMERA, - AppOpsManager.OP_RECORD_AUDIO, + val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA, + AppOpsManager.OP_RECORD_AUDIO) + val OPS_LOCATION = intArrayOf( AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION) + val OPS = OPS_MIC_CAMERA + OPS_LOCATION val intentFilter = IntentFilter().apply { addAction(Intent.ACTION_USER_SWITCHED) addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) } const val TAG = "PrivacyItemController" + private const val ALL_INDICATORS = + SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED + private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED } @VisibleForTesting @@ -74,9 +78,14 @@ class PrivacyItemController @Inject constructor( @Synchronized get() = field.toList() // Returns a shallow copy of the list @Synchronized set - private fun isPermissionsHubEnabled(): Boolean { + private fun isAllIndicatorsEnabled(): Boolean { return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false) + ALL_INDICATORS, false) + } + + private fun isMicCameraEnabled(): Boolean { + return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + MIC_CAMERA, false) } private var currentUserIds = emptyList<Int>() @@ -94,23 +103,28 @@ class PrivacyItemController @Inject constructor( uiExecutor.execute(notifyChanges) } - var indicatorsAvailable = isPermissionsHubEnabled() + var allIndicatorsAvailable = isAllIndicatorsEnabled() private set - @VisibleForTesting - internal val devicePropertiesChangedListener = + var micCameraAvailable = isMicCameraEnabled() + private set + + private val devicePropertiesChangedListener = object : DeviceConfig.OnPropertiesChangedListener { override fun onPropertiesChanged(properties: DeviceConfig.Properties) { if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) && - properties.getKeyset().contains( - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) { - val flag = properties.getBoolean( - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false) - if (indicatorsAvailable != flag) { - // This is happening already in the UI executor, so we can iterate in the - indicatorsAvailable = flag - callbacks.forEach { it.get()?.onFlagChanged(flag) } + (properties.keyset.contains(ALL_INDICATORS) || + properties.keyset.contains(MIC_CAMERA))) { + + // Running on the ui executor so can iterate on callbacks + if (properties.keyset.contains(ALL_INDICATORS)) { + allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false) + callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) } } + if (properties.keyset.contains(MIC_CAMERA)) { + micCameraAvailable = properties.getBoolean(MIC_CAMERA, false) + callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) } + } internalUiExecutor.updateListeningState() } } @@ -123,6 +137,10 @@ class PrivacyItemController @Inject constructor( packageName: String, active: Boolean ) { + // Check if we care about this code right now + if (!allIndicatorsAvailable && code in OPS_LOCATION) { + return + } val userId = UserHandle.getUserId(uid) if (userId in currentUserIds) { update(false) @@ -166,13 +184,16 @@ class PrivacyItemController @Inject constructor( } /** - * Updates listening status based on whether there are callbacks and the indicators are enabled + * Updates listening status based on whether there are callbacks and the indicators are enabled. + * + * Always listen to all OPS so we don't have to figure out what we should be listening to. We + * still have to filter anyway. Updates are filtered in the callback. * * This is only called from private (add/remove)Callback and from the config listener, all in * main thread. */ private fun setListeningState() { - val listen = !callbacks.isEmpty() and indicatorsAvailable + val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable) if (listening == listen) return listening = listen if (listening) { @@ -233,14 +254,19 @@ class PrivacyItemController @Inject constructor( AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE else -> return null } + if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid) return PrivacyItem(type, app) } interface Callback { fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) + + @JvmDefault + fun onFlagAllChanged(flag: Boolean) {} + @JvmDefault - fun onFlagChanged(flag: Boolean) {} + fun onFlagMicCameraChanged(flag: Boolean) {} } internal inner class Receiver : BroadcastReceiver() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 560998b5d1d8..22c735d5fa11 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -185,7 +185,9 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { fakeDragBy(getScrollX() - mScroller.getCurrX()); } else if (isFakeDragging()) { endFakeDrag(); - mBounceAnimatorSet.start(); + if (mBounceAnimatorSet != null) { + mBounceAnimatorSet.start(); + } setOffscreenPageLimit(1); } super.computeScroll(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 2dc82dd853d4..2e258d56ece0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -151,7 +151,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements private Space mSpace; private BatteryMeterView mBatteryRemainingIcon; private RingerModeTracker mRingerModeTracker; - private boolean mPermissionsHubEnabled; + private boolean mAllIndicatorsEnabled; + private boolean mMicCameraIndicatorsEnabled; private PrivacyItemController mPrivacyItemController; private final UiEventLogger mUiEventLogger; @@ -178,13 +179,26 @@ public class QuickStatusBarHeader extends RelativeLayout implements } @Override - public void onFlagChanged(boolean flag) { - if (mPermissionsHubEnabled != flag) { - StatusIconContainer iconContainer = requireViewById(R.id.statusIcons); - iconContainer.setIgnoredSlots(getIgnoredIconSlots()); - setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty()); + public void onFlagAllChanged(boolean flag) { + if (mAllIndicatorsEnabled != flag) { + mAllIndicatorsEnabled = flag; + update(); } } + + @Override + public void onFlagMicCameraChanged(boolean flag) { + if (mMicCameraIndicatorsEnabled != flag) { + mMicCameraIndicatorsEnabled = flag; + update(); + } + } + + private void update() { + StatusIconContainer iconContainer = requireViewById(R.id.statusIcons); + iconContainer.setIgnoredSlots(getIgnoredIconSlots()); + setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty()); + } }; @Inject @@ -267,7 +281,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mRingerModeTextView.setSelected(true); mNextAlarmTextView.setSelected(true); - mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable(); + mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); + mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); } public QuickQSPanel getHeaderQsPanel() { @@ -276,13 +291,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements private List<String> getIgnoredIconSlots() { ArrayList<String> ignored = new ArrayList<>(); - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_camera)); - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_microphone)); - if (mPermissionsHubEnabled) { + if (getChipEnabled()) { + ignored.add(mContext.getResources().getString( + com.android.internal.R.string.status_bar_camera)); ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_location)); + com.android.internal.R.string.status_bar_microphone)); + if (mAllIndicatorsEnabled) { + ignored.add(mContext.getResources().getString( + com.android.internal.R.string.status_bar_location)); + } } return ignored; @@ -300,7 +317,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements } private void setChipVisibility(boolean chipVisible) { - if (chipVisible && mPermissionsHubEnabled) { + if (chipVisible && getChipEnabled()) { mPrivacyChip.setVisibility(View.VISIBLE); // Makes sure that the chip is logged as viewed at most once each time QS is opened // mListening makes sure that the callback didn't return after the user closed QS @@ -607,7 +624,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mAlarmController.addCallback(this); mLifecycle.setCurrentState(Lifecycle.State.RESUMED); // Get the most up to date info - mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable(); + mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); + mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); mPrivacyItemController.addCallback(mPICCallback); } else { mZenController.removeCallback(this); @@ -747,4 +765,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements updateHeaderTextContainerAlphaAnimator(); } } + + private boolean getChipEnabled() { + return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 8c485a6a950f..d2aaaede3f4b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -451,15 +451,17 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy if (listening) { if (mListeners.add(listener) && mListeners.size() == 1) { if (DEBUG) Log.d(TAG, "handleSetListening true"); - mLifecycle.setCurrentState(RESUMED); handleSetListening(listening); - refreshState(); // Ensure we get at least one refresh after listening. + mUiHandler.post(() -> { + mLifecycle.setCurrentState(RESUMED); + refreshState(); // Ensure we get at least one refresh after listening. + }); } } else { if (mListeners.remove(listener) && mListeners.size() == 0) { if (DEBUG) Log.d(TAG, "handleSetListening false"); - mLifecycle.setCurrentState(STARTED); handleSetListening(listening); + mUiHandler.post(() -> mLifecycle.setCurrentState(STARTED)); } } updateIsFullQs(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index eb794a8b4378..c64fc50b8237 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -130,6 +130,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements : mPowerSave ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.icon = mIcon; state.label = mContext.getString(R.string.battery_detail_switch_title); + state.secondaryLabel = ""; state.contentDescription = state.label; state.value = mPowerSave; state.expandedAccessibilityClassName = Switch.class.getName(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java index 3264429a1723..d8548decc5f4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java @@ -67,7 +67,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements private static final String PATTERN_HOUR_MINUTE = "h:mm a"; private static final String PATTERN_HOUR_NINUTE_24 = "HH:mm"; - private final ColorDisplayManager mManager; + private ColorDisplayManager mManager; private final LocationController mLocationController; private final NightDisplayListenerModule.Builder mNightDisplayListenerBuilder; private NightDisplayListener mListener; @@ -126,6 +126,8 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements mListener.setCallback(null); } + mManager = getHost().getUserContext().getSystemService(ColorDisplayManager.class); + // Make a new controller for the new user. mListener = mNightDisplayListenerBuilder.setUser(newUserId).build(); if (mIsListening) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java index 07b841ffb6f7..78975a4798ce 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java @@ -58,10 +58,9 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a"); private final Icon mIcon = ResourceIcon.get( com.android.internal.R.drawable.ic_qs_ui_mode_night); - private final UiModeManager mUiModeManager; + private UiModeManager mUiModeManager; private final BatteryController mBatteryController; private final LocationController mLocationController; - @Inject public UiModeNightTile( QSHost host, @@ -78,7 +77,7 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, activityStarter, qsLogger); mBatteryController = batteryController; - mUiModeManager = mContext.getSystemService(UiModeManager.class); + mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class); mLocationController = locationController; configurationController.observe(getLifecycle(), this); batteryController.observe(getLifecycle(), this); diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt new file mode 100644 index 000000000000..9d05843b42bf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2020 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.systemui.settings + +import android.content.ContentResolver + +interface CurrentUserContentResolverProvider { + + val currentUserContentResolver: ContentResolver +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt index 825a7f3dbadb..d7c4caaa4f9d 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt @@ -16,6 +16,7 @@ package com.android.systemui.settings +import android.content.ContentResolver import android.content.Context import android.os.UserHandle import androidx.annotation.VisibleForTesting @@ -31,7 +32,7 @@ import java.lang.IllegalStateException class CurrentUserContextTracker internal constructor( private val sysuiContext: Context, broadcastDispatcher: BroadcastDispatcher -) { +) : CurrentUserContentResolverProvider { private val userTracker: CurrentUserTracker private var initialized = false @@ -44,6 +45,9 @@ class CurrentUserContextTracker internal constructor( return _curUserContext!! } + override val currentUserContentResolver: ContentResolver + get() = currentUserContext.contentResolver + init { userTracker = object : CurrentUserTracker(broadcastDispatcher) { override fun onUserSwitched(newUserId: Int) { @@ -54,8 +58,8 @@ class CurrentUserContextTracker internal constructor( fun initialize() { initialized = true - _curUserContext = makeUserContext(userTracker.currentUserId) userTracker.startTracking() + _curUserContext = makeUserContext(userTracker.currentUserId) } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java index 2c5c3ceb6e66..eb5bd5c01a78 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java +++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java @@ -19,10 +19,12 @@ package com.android.systemui.settings.dagger; import android.content.Context; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.settings.CurrentUserContentResolverProvider; import com.android.systemui.settings.CurrentUserContextTracker; import javax.inject.Singleton; +import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -30,7 +32,7 @@ import dagger.Provides; * Dagger Module for classes found within the com.android.systemui.settings package. */ @Module -public interface SettingsModule { +public abstract class SettingsModule { /** * Provides and initializes a CurrentUserContextTracker @@ -45,4 +47,9 @@ public interface SettingsModule { tracker.initialize(); return tracker; } + + @Binds + @Singleton + abstract CurrentUserContentResolverProvider bindCurrentUserContentResolverTracker( + CurrentUserContextTracker tracker); } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index 4007abb39903..d40b666860e9 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -78,6 +78,22 @@ public class Divider extends SystemUI { onUndockingTask(); } } + + @Override + public void onActivityForcedResizable(String packageName, int taskId, + int reason) { + mDividerController.onActivityForcedResizable(packageName, taskId, reason); + } + + @Override + public void onActivityDismissingDockedStack() { + mDividerController.onActivityDismissingSplitScreen(); + } + + @Override + public void onActivityLaunchOnSecondaryDisplayFailed() { + mDividerController.onActivityLaunchOnSecondaryDisplayFailed(); + } } ); } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java index 81649f608581..1ee8abb411b9 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java @@ -50,8 +50,7 @@ import java.util.function.Consumer; /** * Controls the docked stack divider. */ -public class DividerController implements DividerView.DividerCallbacks, - DisplayController.OnDisplaysChangedListener { +public class DividerController implements DisplayController.OnDisplaysChangedListener { static final boolean DEBUG = false; private static final String TAG = "Divider"; @@ -257,8 +256,8 @@ public class DividerController implements DividerView.DividerCallbacks, mView = (DividerView) LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null); DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId()); - mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout, - mImePositionProcessor, mWindowManagerProxy); + mView.injectDependencies(mWindowManager, mDividerState, mForcedResizableController, mSplits, + mSplitLayout, mImePositionProcessor, mWindowManagerProxy); mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE); mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */); final int size = dctx.getResources().getDimensionPixelSize( @@ -397,7 +396,22 @@ public class DividerController implements DividerView.DividerCallbacks, } } - /** Called when there's a task undocking. */ + /** Called when there's an activity forced resizable. */ + public void onActivityForcedResizable(String packageName, int taskId, int reason) { + mForcedResizableController.activityForcedResizable(packageName, taskId, reason); + } + + /** Called when there's an activity dismissing split screen. */ + public void onActivityDismissingSplitScreen() { + mForcedResizableController.activityDismissingSplitScreen(); + } + + /** Called when there's an activity launch on secondary display failed. */ + public void onActivityLaunchOnSecondaryDisplayFailed() { + mForcedResizableController.activityLaunchOnSecondaryDisplayFailed(); + } + + /** Called when there's a task undocking. */ public void onUndockingTask() { if (mView != null) { mView.onUndockingTask(); @@ -426,17 +440,7 @@ public class DividerController implements DividerView.DividerCallbacks, mForcedResizableController.onAppTransitionFinished(); } - @Override - public void onDraggingStart() { - mForcedResizableController.onDraggingStart(); - } - - @Override - public void onDraggingEnd() { - mForcedResizableController.onDraggingEnd(); - } - - /** Dumps current status of Divider.*/ + /** Dumps current status of Split Screen. */ public void dump(PrintWriter pw) { pw.print(" mVisible="); pw.println(mVisible); pw.print(" mMinimized="); pw.println(mMinimized); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java index f412cc00981b..ff8bab07db05 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java @@ -28,15 +28,13 @@ import android.util.ArraySet; import android.widget.Toast; import com.android.systemui.R; -import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.TaskStackChangeListener; import java.util.function.Consumer; /** * Controller that decides when to show the {@link ForcedResizableInfoActivity}. */ -public class ForcedResizableInfoActivityController { +final class ForcedResizableInfoActivityController implements DividerView.DividerCallbacks { private static final String SELF_PACKAGE_NAME = "com.android.systemui"; @@ -47,12 +45,7 @@ public class ForcedResizableInfoActivityController { private final ArraySet<String> mPackagesShownInSession = new ArraySet<>(); private boolean mDividerDragging; - private final Runnable mTimeoutRunnable = new Runnable() { - @Override - public void run() { - showPending(); - } - }; + private final Runnable mTimeoutRunnable = this::showPending; private final Consumer<Boolean> mDockedStackExistsListener = exists -> { if (!exists) { @@ -78,44 +71,28 @@ public class ForcedResizableInfoActivityController { public ForcedResizableInfoActivityController(Context context, DividerController dividerController) { mContext = context; - ActivityManagerWrapper.getInstance().registerTaskStackListener( - new TaskStackChangeListener() { - @Override - public void onActivityForcedResizable(String packageName, int taskId, - int reason) { - activityForcedResizable(packageName, taskId, reason); - } - - @Override - public void onActivityDismissingDockedStack() { - activityDismissingDockedStack(); - } - - @Override - public void onActivityLaunchOnSecondaryDisplayFailed() { - activityLaunchOnSecondaryDisplayFailed(); - } - }); dividerController.registerInSplitScreenListener(mDockedStackExistsListener); } - public void onAppTransitionFinished() { - if (!mDividerDragging) { - showPending(); - } - } - - void onDraggingStart() { + @Override + public void onDraggingStart() { mDividerDragging = true; mHandler.removeCallbacks(mTimeoutRunnable); } - void onDraggingEnd() { + @Override + public void onDraggingEnd() { mDividerDragging = false; showPending(); } - private void activityForcedResizable(String packageName, int taskId, int reason) { + void onAppTransitionFinished() { + if (!mDividerDragging) { + showPending(); + } + } + + void activityForcedResizable(String packageName, int taskId, int reason) { if (debounce(packageName)) { return; } @@ -123,12 +100,12 @@ public class ForcedResizableInfoActivityController { postTimeout(); } - private void activityDismissingDockedStack() { + void activityDismissingSplitScreen() { Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT).show(); } - private void activityLaunchOnSecondaryDisplayFailed() { + void activityLaunchOnSecondaryDisplayFailed() { Toast.makeText(mContext, R.string.activity_launch_on_secondary_display_failed_text, Toast.LENGTH_SHORT).show(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 99cb4760a8d9..a87311a69ab5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -2620,6 +2620,7 @@ public class NotificationPanelViewController extends PanelViewController { super.onClosingFinished(); resetHorizontalPanelPosition(); setClosingWithAlphaFadeout(false); + mMediaHierarchyManager.closeGuts(); } private void setClosingWithAlphaFadeout(boolean closing) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index b2cfceae2cf6..8cb54eef01b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -662,16 +662,18 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotCamera, showCamera); mIconController.setIconVisibility(mSlotMicrophone, showMicrophone); - mIconController.setIconVisibility(mSlotLocation, showLocation); + if (mPrivacyItemController.getAllIndicatorsAvailable()) { + mIconController.setIconVisibility(mSlotLocation, showLocation); + } } @Override public void onLocationActiveChanged(boolean active) { - if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation(); + if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController(); } // Updates the status view based on the current state of location requests. - private void updateLocation() { + private void updateLocationFromController() { if (mLocationController.isLocationActive()) { mIconController.setIconVisibility(mSlotLocation, true); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index b89cb210dea1..8ff7a41cb22f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -86,7 +86,7 @@ public interface StatusBarIconController { static ArraySet<String> getIconHideList(Context context, String hideListStr) { ArraySet<String> ret = new ArraySet<>(); String[] hideList = hideListStr == null - ? context.getResources().getStringArray(R.array.config_statusBarIconBlackList) + ? context.getResources().getStringArray(R.array.config_statusBarIconsToExclude) : hideListStr.split(","); for (String slot : hideList) { if (!TextUtils.isEmpty(slot)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index 673549ab589f..e5a46797d035 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -79,6 +79,13 @@ public interface BatteryController extends DemoMode, Dumpable, default void setReverseState(boolean isReverse) {} /** + * Returns {@code true} if extreme battery saver is on. + */ + default boolean isExtremeSaverOn() { + return false; + } + + /** * A listener that will be notified whenever a change in battery level or power save mode has * occurred. */ @@ -92,6 +99,9 @@ public interface BatteryController extends DemoMode, Dumpable, default void onReverseChanged(boolean isReverse, int level, String name) { } + + default void onExtremeBatterySaverChanged(boolean isExtreme) { + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java index b5f98ad47c09..89297fd83bb0 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java @@ -54,7 +54,7 @@ public class UsbAccessoryUriActivity extends AlertActivity String uriString = intent.getStringExtra("uri"); mUri = (uriString == null ? null : Uri.parse(uriString)); - // sanity check before displaying dialog + // Exception check before displaying dialog if (mUri == null) { Log.e(TAG, "could not parse Uri " + uriString); finish(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt new file mode 100644 index 000000000000..4d0f2ed47495 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2020 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.systemui.controls + +import android.content.ComponentName +import android.graphics.drawable.Icon +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class CustomIconCacheTest : SysuiTestCase() { + + companion object { + private val TEST_COMPONENT1 = ComponentName.unflattenFromString("pkg/.cls1")!! + private val TEST_COMPONENT2 = ComponentName.unflattenFromString("pkg/.cls2")!! + private const val CONTROL_ID_1 = "TEST_CONTROL_1" + private const val CONTROL_ID_2 = "TEST_CONTROL_2" + } + + @Mock(stubOnly = true) + private lateinit var icon1: Icon + @Mock(stubOnly = true) + private lateinit var icon2: Icon + private lateinit var customIconCache: CustomIconCache + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + customIconCache = CustomIconCache() + } + + @Test + fun testIconStoredCorrectly() { + customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1) + + assertTrue(icon1 === customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_1)) + } + + @Test + fun testIconNotStoredReturnsNull() { + customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1) + + assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_2)) + } + + @Test + fun testWrongComponentReturnsNull() { + customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1) + + assertNull(customIconCache.retrieve(TEST_COMPONENT2, CONTROL_ID_1)) + } + + @Test + fun testChangeComponentOldComponentIsRemoved() { + customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1) + customIconCache.store(TEST_COMPONENT2, CONTROL_ID_2, icon2) + + assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_1)) + assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_2)) + } + + @Test + fun testChangeComponentCorrectIconRetrieved() { + customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1) + customIconCache.store(TEST_COMPONENT2, CONTROL_ID_1, icon2) + + assertTrue(icon2 === customIconCache.retrieve(TEST_COMPONENT2, CONTROL_ID_1)) + } + + @Test + fun testStoreNull() { + customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1) + customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, null) + + assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_1)) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt index ce33a8d49fac..f0003ed603ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt @@ -22,6 +22,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.ControlInterface +import com.android.systemui.controls.CustomIconCache import com.android.systemui.controls.controller.ControlInfo import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -57,6 +58,8 @@ class FavoritesModelTest : SysuiTestCase() { private lateinit var callback: FavoritesModel.FavoritesModelCallback @Mock private lateinit var adapter: RecyclerView.Adapter<*> + @Mock + private lateinit var customIconCache: CustomIconCache private lateinit var model: FavoritesModel private lateinit var dividerWrapper: DividerWrapper @@ -64,7 +67,7 @@ class FavoritesModelTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - model = FavoritesModel(TEST_COMPONENT, INITIAL_FAVORITES, callback) + model = FavoritesModel(customIconCache, TEST_COMPONENT, INITIAL_FAVORITES, callback) model.attachAdapter(adapter) dividerWrapper = model.elements.first { it is DividerWrapper } as DividerWrapper } @@ -89,7 +92,7 @@ class FavoritesModelTest : SysuiTestCase() { @Test fun testInitialElements() { val expected = INITIAL_FAVORITES.map { - ControlInfoWrapper(TEST_COMPONENT, it, true) + ControlInfoWrapper(TEST_COMPONENT, it, true, customIconCache::retrieve) } + DividerWrapper() assertEquals(expected, model.elements) } @@ -287,5 +290,13 @@ class FavoritesModelTest : SysuiTestCase() { verify(callback).onFirstChange() } + @Test + fun testCacheCalledWhenGettingCustomIcon() { + val wrapper = model.elements[0] as ControlInfoWrapper + wrapper.customIcon + + verify(customIconCache).retrieve(TEST_COMPONENT, wrapper.controlId) + } + private fun getDividerPosition(): Int = model.elements.indexOf(dividerWrapper) }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index ac8c6710e041..4c5495474018 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -88,7 +88,7 @@ import java.util.concurrent.Executor; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper() +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class GlobalActionsDialogTest extends SysuiTestCase { private GlobalActionsDialog mGlobalActionsDialog; diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index c63781cb110a..8a30b00e609d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.media +import android.content.Intent import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.GradientDrawable @@ -23,6 +24,7 @@ import android.graphics.drawable.RippleDrawable import android.media.MediaMetadata import android.media.session.MediaSession import android.media.session.PlaybackState +import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View @@ -35,24 +37,31 @@ import android.widget.TextView import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.LiveData import androidx.test.filters.SmallTest -import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.util.animation.TransitionLayout import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat +import dagger.Lazy import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock +import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.mock +import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever private const val KEY = "TEST_KEY" private const val APP = "APP" @@ -81,6 +90,8 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var seekBarViewModel: SeekBarViewModel @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress> @Mock private lateinit var mediaViewController: MediaViewController + @Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil + @Mock private lateinit var mediaDataManager: MediaDataManager @Mock private lateinit var expandedSet: ConstraintSet @Mock private lateinit var collapsedSet: ConstraintSet private lateinit var appIcon: ImageView @@ -100,6 +111,9 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var action2: ImageButton private lateinit var action3: ImageButton private lateinit var action4: ImageButton + private lateinit var settings: View + private lateinit var cancel: View + private lateinit var dismiss: View private lateinit var session: MediaSession private val device = MediaDeviceData(true, null, DEVICE_NAME) @@ -114,7 +128,7 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet) player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController, - seekBarViewModel) + seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil) whenever(seekBarViewModel.progress).thenReturn(seekBarData) // Mock out a view holder for the player to attach to. @@ -156,6 +170,12 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(holder.action3).thenReturn(action3) action4 = ImageButton(context) whenever(holder.action4).thenReturn(action4) + settings = View(context) + whenever(holder.settings).thenReturn(settings) + cancel = View(context) + whenever(holder.cancel).thenReturn(cancel) + dismiss = View(context) + whenever(holder.dismiss).thenReturn(dismiss) // Create media session val metadataBuilder = MediaMetadata.Builder().apply { @@ -254,4 +274,79 @@ public class MediaControlPanelTest : SysuiTestCase() { assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME) assertThat(seamless.isEnabled()).isFalse() } + + @Test + fun longClick_gutsClosed() { + player.attach(holder) + whenever(mediaViewController.isGutsVisible).thenReturn(false) + + val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) + verify(holder.player).setOnLongClickListener(captor.capture()) + + captor.value.onLongClick(holder.player) + verify(mediaViewController).openGuts() + } + + @Test + fun longClick_gutsOpen() { + player.attach(holder) + whenever(mediaViewController.isGutsVisible).thenReturn(true) + + val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) + verify(holder.player).setOnLongClickListener(captor.capture()) + + captor.value.onLongClick(holder.player) + verify(mediaViewController, never()).openGuts() + } + + @Test + fun cancelButtonClick_animation() { + player.attach(holder) + + cancel.callOnClick() + + verify(mediaViewController).closeGuts(false) + } + + @Test + fun settingsButtonClick() { + player.attach(holder) + + settings.callOnClick() + + val captor = ArgumentCaptor.forClass(Intent::class.java) + verify(activityStarter).startActivity(captor.capture(), eq(true)) + + assertThat(captor.value.action).isEqualTo(ACTION_MEDIA_CONTROLS_SETTINGS) + } + + @Test + fun dismissButtonClick() { + player.attach(holder) + val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), + emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null, + notificationKey = KEY) + player.bind(state) + + dismiss.callOnClick() + val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java) + verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean()) + + captor.value.onDismiss() + verify(mediaDataManager).dismissMediaData(eq(KEY), anyLong()) + } + + @Test + fun dismissButtonClick_nullNotificationKey() { + player.attach(holder) + val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), + emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null) + player.bind(state) + + verify(keyguardDismissUtil, never()) + .executeWhenUnlocked( + any(ActivityStarter.OnDismissAction::class.java), + ArgumentMatchers.anyBoolean() + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 59c2d0e86c56..3789e6ef1f65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -217,6 +217,20 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(data.actions).hasSize(1) } + @Test + fun testDismissMedia_listenerCalled() { + val listener = mock(MediaDataManager.Listener::class.java) + mediaDataManager.addListener(listener) + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) + mediaDataManager.dismissMediaData(KEY, 0L) + + foregroundExecutor.advanceClockToLast() + foregroundExecutor.runAllReady() + + verify(listener).onMediaDataRemoved(eq(KEY)) + } + /** * Simple implementation of [MediaDataManager.Listener] for the test. * diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt index 91c5ff8ee627..d86dfa5fa5f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt @@ -142,4 +142,11 @@ class MediaHierarchyManagerTest : SysuiTestCase() { verify(mediaCarouselController).onDesiredLocationChanged(ArgumentMatchers.anyInt(), any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong()) } + + @Test + fun testCloseGutsRelayToCarousel() { + mediaHiearchyManager.closeGuts() + + verify(mediaCarouselController).closeGuts() + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java index 96bb521a5d5b..9f67722041aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java @@ -37,6 +37,7 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.FloatingContentCoordinator; @@ -85,6 +86,9 @@ public class PipTouchHandlerTest extends SysuiTestCase { @Mock private SysUiState mSysUiState; + @Mock + private PipUiEventLogger mPipUiEventLogger; + private PipSnapAlgorithm mPipSnapAlgorithm; private PipMotionHelper mMotionHelper; private PipResizeGestureHandler mPipResizeGestureHandler; @@ -104,7 +108,7 @@ public class PipTouchHandlerTest extends SysuiTestCase { mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager, mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy, - mPipSnapAlgorithm, mSysUiState); + mPipSnapAlgorithm, mSysUiState, mPipUiEventLogger); mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper()); mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler()); mPipTouchHandler.setPipMotionHelper(mMotionHelper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt new file mode 100644 index 000000000000..4ba29e6e02a6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2020 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.systemui.privacy + +import android.os.UserManager +import android.provider.DeviceConfig +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags +import com.android.systemui.SysuiTestCase +import com.android.systemui.appops.AppOpsController +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dump.DumpManager +import com.android.systemui.util.DeviceConfigProxy +import com.android.systemui.util.DeviceConfigProxyFake +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class PrivacyItemControllerFlagsTest : SysuiTestCase() { + companion object { + fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() + fun <T> eq(value: T): T = Mockito.eq(value) ?: value + fun <T> any(): T = Mockito.any<T>() + + private const val ALL_INDICATORS = + SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED + private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED + } + + @Mock + private lateinit var appOpsController: AppOpsController + @Mock + private lateinit var callback: PrivacyItemController.Callback + @Mock + private lateinit var userManager: UserManager + @Mock + private lateinit var broadcastDispatcher: BroadcastDispatcher + @Mock + private lateinit var dumpManager: DumpManager + + private lateinit var privacyItemController: PrivacyItemController + private lateinit var executor: FakeExecutor + private lateinit var deviceConfigProxy: DeviceConfigProxy + + fun PrivacyItemController(): PrivacyItemController { + return PrivacyItemController( + appOpsController, + executor, + executor, + broadcastDispatcher, + deviceConfigProxy, + userManager, + dumpManager + ) + } + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + executor = FakeExecutor(FakeSystemClock()) + deviceConfigProxy = DeviceConfigProxyFake() + + privacyItemController = PrivacyItemController() + privacyItemController.addCallback(callback) + + executor.runAllReady() + } + + @Test + fun testNotListeningByDefault() { + assertFalse(privacyItemController.allIndicatorsAvailable) + assertFalse(privacyItemController.micCameraAvailable) + + verify(appOpsController, never()).addCallback(any(), any()) + } + + @Test + fun testMicCameraChanged() { + changeMicCamera(true) + executor.runAllReady() + + verify(callback).onFlagMicCameraChanged(true) + verify(callback, never()).onFlagAllChanged(anyBoolean()) + + assertTrue(privacyItemController.micCameraAvailable) + assertFalse(privacyItemController.allIndicatorsAvailable) + } + + @Test + fun testAllChanged() { + changeAll(true) + executor.runAllReady() + + verify(callback).onFlagAllChanged(true) + verify(callback, never()).onFlagMicCameraChanged(anyBoolean()) + + assertTrue(privacyItemController.allIndicatorsAvailable) + assertFalse(privacyItemController.micCameraAvailable) + } + + @Test + fun testBothChanged() { + changeAll(true) + changeMicCamera(true) + executor.runAllReady() + + verify(callback, atLeastOnce()).onFlagAllChanged(true) + verify(callback, atLeastOnce()).onFlagMicCameraChanged(true) + + assertTrue(privacyItemController.allIndicatorsAvailable) + assertTrue(privacyItemController.micCameraAvailable) + } + + @Test + fun testAll_listeningToAll() { + changeAll(true) + executor.runAllReady() + + verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) + } + + @Test + fun testMicCamera_listening() { + changeMicCamera(true) + executor.runAllReady() + + verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) + } + + @Test + fun testAll_listening() { + changeAll(true) + executor.runAllReady() + + verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) + } + + @Test + fun testAllFalse_notListening() { + changeAll(true) + executor.runAllReady() + changeAll(false) + executor.runAllReady() + + verify(appOpsController).removeCallback(any(), any()) + } + + @Test + fun testSomeListening_stillListening() { + changeAll(true) + changeMicCamera(true) + executor.runAllReady() + changeAll(false) + executor.runAllReady() + + verify(appOpsController, never()).removeCallback(any(), any()) + } + + @Test + fun testAllDeleted_stopListening() { + changeAll(true) + executor.runAllReady() + changeAll(null) + executor.runAllReady() + + verify(appOpsController).removeCallback(any(), any()) + } + + @Test + fun testMicDeleted_stopListening() { + changeMicCamera(true) + executor.runAllReady() + changeMicCamera(null) + executor.runAllReady() + + verify(appOpsController).removeCallback(any(), any()) + } + + private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) + private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) + + private fun changeProperty(name: String, value: Boolean?) { + deviceConfigProxy.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + name, + value?.toString(), + false + ) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt index dddc35072315..fb42baaa0cb5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.privacy import android.app.ActivityManager import android.app.AppOpsManager -import android.content.Context import android.content.Intent import android.content.pm.UserInfo import android.os.UserHandle @@ -69,10 +68,11 @@ class PrivacyItemControllerTest : SysuiTestCase() { companion object { val CURRENT_USER_ID = ActivityManager.getCurrentUser() val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE - const val SYSTEM_UID = 1000 const val TEST_PACKAGE_NAME = "test" - const val DEVICE_SERVICES_STRING = "Device services" - const val TAG = "PrivacyItemControllerTest" + + private const val ALL_INDICATORS = + SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED + private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() fun <T> eq(value: T): T = Mockito.eq(value) ?: value fun <T> any(): T = Mockito.any<T>() @@ -97,9 +97,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { private lateinit var executor: FakeExecutor private lateinit var deviceConfigProxy: DeviceConfigProxy - fun PrivacyItemController(context: Context): PrivacyItemController { + fun PrivacyItemController(): PrivacyItemController { return PrivacyItemController( - context, appOpsController, executor, executor, @@ -116,11 +115,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { executor = FakeExecutor(FakeSystemClock()) deviceConfigProxy = DeviceConfigProxyFake() - appOpsController = mDependency.injectMockDependency(AppOpsController::class.java) - - deviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, - "true", false) + // Listen to everything by default + changeAll(true) doReturn(listOf(object : UserInfo() { init { @@ -128,7 +124,7 @@ class PrivacyItemControllerTest : SysuiTestCase() { } })).`when`(userManager).getProfiles(anyInt()) - privacyItemController = PrivacyItemController(mContext) + privacyItemController = PrivacyItemController() } @Test @@ -276,15 +272,59 @@ class PrivacyItemControllerTest : SysuiTestCase() { @Test fun testNotListeningWhenIndicatorsDisabled() { - deviceConfigProxy.setProperty( - DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, - "false", - false - ) + changeAll(false) privacyItemController.addCallback(callback) executor.runAllReady() verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS), any()) } + + @Test + fun testNotSendingLocationWhenOnlyMicCamera() { + changeAll(false) + changeMicCamera(true) + executor.runAllReady() + + doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0), + AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) + .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + + privacyItemController.addCallback(callback) + executor.runAllReady() + + verify(callback).onPrivacyItemsChanged(capture(argCaptor)) + + assertEquals(1, argCaptor.value.size) + assertEquals(PrivacyType.TYPE_CAMERA, argCaptor.value[0].privacyType) + } + + @Test + fun testNotUpdated_LocationChangeWhenOnlyMicCamera() { + doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) + .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + + privacyItemController.addCallback(callback) + changeAll(false) + changeMicCamera(true) + executor.runAllReady() + reset(callback) // Clean callback + + verify(appOpsController).addCallback(any(), capture(argCaptorCallback)) + argCaptorCallback.value.onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true) + + verify(callback, never()).onPrivacyItemsChanged(any()) + } + + private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) + private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) + + private fun changeProperty(name: String, value: Boolean?) { + deviceConfigProxy.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + name, + value?.toString(), + false + ) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index 103e5586f395..cccb65d11228 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -74,7 +74,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) @SmallTest public class QSTileImplTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java index 2d276bb876f3..6b54791dd143 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java @@ -47,7 +47,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper() +@TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class ScreenRecordTileTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java index ae87eefd243c..781f875fd868 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java @@ -41,7 +41,7 @@ import org.junit.runner.RunWith; import org.mockito.Mockito; @RunWith(AndroidTestingRunner.class) -@RunWithLooper() +@RunWithLooper(setAsMainLooper = true) @SmallTest public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java index a16fb5e8dc17..86dacc13feab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java @@ -37,7 +37,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) @RunWith(AndroidTestingRunner.class) @SmallTest public class CallbackControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java index 0e1c560c918b..b2f57d0726cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java @@ -39,7 +39,7 @@ import com.android.systemui.SysuiBaseFragmentTest; import org.junit.Test; import org.junit.runner.RunWith; -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) @RunWith(AndroidTestingRunner.class) @SmallTest public class LifecycleFragmentTest extends SysuiBaseFragmentTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java index 486939d1f08e..4f509eaaadde 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java @@ -49,7 +49,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) @RunWith(AndroidTestingRunner.class) @SmallTest public class SysuiLifecycleTest extends SysuiTestCase { diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml index 9b9dcde910e7..5f8d2997197f 100644 --- a/packages/Tethering/res/values/config.xml +++ b/packages/Tethering/res/values/config.xml @@ -73,6 +73,9 @@ <!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. --> <bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool> + <!-- Use legacy wifi p2p dedicated address instead of randomize address. --> + <bool translatable="false" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip">false</bool> + <!-- Dhcp range (min, max) to use for tethering purposes --> <string-array translatable="false" name="config_tether_dhcp_range"> </string-array> diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml index 6a33d55cb0de..0ee7a992ee20 100644 --- a/packages/Tethering/res/values/overlayable.xml +++ b/packages/Tethering/res/values/overlayable.xml @@ -30,6 +30,7 @@ --> <item type="bool" name="config_tether_enable_bpf_offload"/> <item type="bool" name="config_tether_enable_legacy_dhcp_server"/> + <item type="bool" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip"/> <item type="integer" name="config_tether_offload_poll_interval"/> <item type="array" name="config_tether_upstream_types"/> <item type="bool" name="config_tether_upstream_automatic"/> diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java index aa58a4b6a320..fd9e36080c80 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java @@ -15,6 +15,8 @@ */ package com.android.networkstack.tethering; +import static android.net.TetheringManager.TETHERING_WIFI_P2P; + import static java.util.Arrays.asList; import android.content.Context; @@ -58,6 +60,7 @@ public class PrivateAddressCoordinator { private static final int BYTE_MASK = 0xff; // reserved for bluetooth tethering. private static final int BLUETOOTH_RESERVED = 44; + private static final int WIFI_P2P_RESERVED = 49; private static final byte DEFAULT_ID = (byte) 42; // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream @@ -71,15 +74,18 @@ public class PrivateAddressCoordinator { // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers. private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16"; + private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24"; private final IpPrefix mTetheringPrefix; private final ConnectivityManager mConnectivityMgr; + private final TetheringConfiguration mConfig; - public PrivateAddressCoordinator(Context context) { + public PrivateAddressCoordinator(Context context, TetheringConfiguration config) { mDownstreams = new ArraySet<>(); mUpstreamPrefixMap = new ArrayMap<>(); mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX); mConnectivityMgr = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE); + mConfig = config; } /** @@ -141,12 +147,21 @@ public class PrivateAddressCoordinator { mUpstreamPrefixMap.removeAll(toBeRemoved); } + private boolean isReservedSubnet(final int subnet) { + return subnet == BLUETOOTH_RESERVED || subnet == WIFI_P2P_RESERVED; + } + /** * Pick a random available address and mark its prefix as in use for the provided IpServer, * returns null if there is no available address. */ @Nullable public LinkAddress requestDownstreamAddress(final IpServer ipServer) { + if (mConfig.shouldEnableWifiP2pDedicatedIp() + && ipServer.interfaceType() == TETHERING_WIFI_P2P) { + return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS); + } + // Address would be 192.168.[subAddress]/24. final byte[] bytes = mTetheringPrefix.getRawAddress(); final int subAddress = getRandomSubAddr(); @@ -154,7 +169,7 @@ public class PrivateAddressCoordinator { bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff); for (int i = 0; i < MAX_UBYTE; i++) { final int newSubNet = (subNet + i) & BYTE_MASK; - if (newSubNet == BLUETOOTH_RESERVED) continue; + if (isReservedSubnet(newSubNet)) continue; bytes[2] = (byte) newSubNet; final InetAddress addr; diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java index cfc657587332..7dd5290ee83b 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -320,10 +320,13 @@ public class Tethering { mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); mNetdCallback = new NetdCallback(); - mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext); // Load tethering configuration. updateConfiguration(); + // It is OK for the configuration to be passed to the PrivateAddressCoordinator at + // construction time because the only part of the configuration it uses is + // shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that. + mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext, mConfig); // Must be initialized after tethering configuration is loaded because BpfCoordinator // constructor needs to use the configuration. diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index e1771a561370..5783805861a3 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -84,6 +84,9 @@ public class TetheringConfiguration { public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = "tether_enable_legacy_dhcp_server"; + public static final String USE_LEGACY_WIFI_P2P_DEDICATED_IP = + "use_legacy_wifi_p2p_dedicated_ip"; + /** * Default value that used to periodic polls tether offload stats from tethering offload HAL * to make the data warnings work. @@ -113,6 +116,7 @@ public class TetheringConfiguration { private final int mOffloadPollInterval; // TODO: Add to TetheringConfigurationParcel if required. private final boolean mEnableBpfOffload; + private final boolean mEnableWifiP2pDedicatedIp; public TetheringConfiguration(Context ctx, SharedLog log, int id) { final SharedLog configLog = log.forSubComponent("config"); @@ -156,6 +160,10 @@ public class TetheringConfiguration { R.integer.config_tether_offload_poll_interval, DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); + mEnableWifiP2pDedicatedIp = getResourceBoolean(res, + R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip, + false /* defaultValue */); + configLog.log(toString()); } @@ -199,6 +207,11 @@ public class TetheringConfiguration { return !TextUtils.isEmpty(provisioningAppNoUi); } + /** Check whether dedicated wifi p2p address is enabled. */ + public boolean shouldEnableWifiP2pDedicatedIp() { + return mEnableWifiP2pDedicatedIp; + } + /** Does the dumping.*/ public void dump(PrintWriter pw) { pw.print("activeDataSubId: "); @@ -233,6 +246,9 @@ public class TetheringConfiguration { pw.print("enableLegacyDhcpServer: "); pw.println(enableLegacyDhcpServer); + + pw.print("enableWifiP2pDedicatedIp: "); + pw.println(mEnableWifiP2pDedicatedIp); } /** Returns the string representation of this object.*/ diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java index 2c0df6fc6327..8e93c2e447b3 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java @@ -15,6 +15,11 @@ */ package com.android.networkstack.tethering; +import static android.net.TetheringManager.TETHERING_ETHERNET; +import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHERING_WIFI_P2P; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.mockito.Mockito.never; @@ -54,22 +59,34 @@ public final class PrivateAddressCoordinatorTest { @Mock private IpServer mHotspotIpServer; @Mock private IpServer mUsbIpServer; @Mock private IpServer mEthernetIpServer; + @Mock private IpServer mWifiP2pIpServer; @Mock private Context mContext; @Mock private ConnectivityManager mConnectivityMgr; + @Mock private TetheringConfiguration mConfig; private PrivateAddressCoordinator mPrivateAddressCoordinator; private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24"); + private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24"); private final Network mWifiNetwork = new Network(1); private final Network mMobileNetwork = new Network(2); private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork}; + private void setUpIpServers() throws Exception { + when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB); + when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET); + when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI); + when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P); + } + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr); when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks); - mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext)); + when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false); + setUpIpServers(); + mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig)); } @Test @@ -256,4 +273,38 @@ public final class PrivateAddressCoordinatorTest { final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr); assertEquals(predefinedPrefix, ethPrefix); } + + private int getSubAddress(final byte... ipv4Address) { + assertEquals(4, ipv4Address.length); + + int subnet = Byte.toUnsignedInt(ipv4Address[2]); + return (subnet << 8) + ipv4Address[3]; + } + + private void assertReseveredWifiP2pPrefix() throws Exception { + LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address); + final IpPrefix legacyWifiP2pPrefix = PrefixUtils.asIpPrefix(mLegacyWifiP2pAddress); + assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix); + mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + } + + @Test + public void testEnableLegacyWifiP2PAddress() throws Exception { + when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn( + getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress())); + // No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix + // is resevered. + assertReseveredWifiP2pPrefix(); + + when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(true); + assertReseveredWifiP2pPrefix(); + + // If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address. + LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( + mWifiP2pIpServer); + assertEquals(mLegacyWifiP2pAddress, address); + mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer); + } } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index a9ac4e2851f3..dc0940cc0222 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -128,6 +128,8 @@ public class TetheringConfigurationTest { .thenReturn(new String[0]); when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip)) + .thenReturn(false); initializeBpfOffloadConfiguration(true, null /* unset */); mHasTelephonyManager = true; @@ -413,4 +415,17 @@ public class TetheringConfigurationTest { R.string.config_mobile_hotspot_provision_response)).thenReturn( PROVISIONING_APP_RESPONSE); } + + @Test + public void testEnableLegacyWifiP2PAddress() throws Exception { + final TetheringConfiguration defaultCfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertFalse(defaultCfg.shouldEnableWifiP2pDedicatedIp()); + + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip)) + .thenReturn(true); + final TetheringConfiguration testCfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp()); + } } diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index b0e401bdda8a..3f712dd1492f 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -3706,33 +3706,40 @@ message MetricsEvent { // OS: O BACKUP_SETTINGS = 818; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // ACTION: Picture-in-picture was explicitly entered for an activity // VALUE: true if it was entered while hiding as a result of moving to // another task, false otherwise - ACTION_PICTURE_IN_PICTURE_ENTERED = 819; + ACTION_PICTURE_IN_PICTURE_ENTERED = 819 [deprecated=true]; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // ACTION: The activity currently in picture-in-picture was expanded back to fullscreen // PACKAGE: The package name of the activity that was expanded back to fullscreen - ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN = 820; + ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN = 820 [deprecated=true]; + // DEPRECATED: The metrics no longer used after migration to UiEvent per go/uievent. // ACTION: The activity currently in picture-in-picture was minimized // VALUE: True if the PiP was minimized, false otherwise - ACTION_PICTURE_IN_PICTURE_MINIMIZED = 821; + ACTION_PICTURE_IN_PICTURE_MINIMIZED = 821 [deprecated=true]; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // ACTION: Picture-in-picture was dismissed via the dismiss button // VALUE: 0 if dismissed by tap, 1 if dismissed by drag - ACTION_PICTURE_IN_PICTURE_DISMISSED = 822; + ACTION_PICTURE_IN_PICTURE_DISMISSED = 822 [deprecated=true]; - // ACTION: The visibility of the picture-in-picture meny + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. + // ACTION: The visibility of the picture-in-picture menu // VALUE: Whether or not the menu is visible - ACTION_PICTURE_IN_PICTURE_MENU = 823; + ACTION_PICTURE_IN_PICTURE_MENU = 823 [deprecated=true]; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // Enclosing category for group of PICTURE_IN_PICTURE_ASPECT_RATIO_FOO events, // logged when the aspect ratio changes - ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED = 824; + ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED = 824 [deprecated=true]; + // DEPRECATED: The metrics no longer used after migration to UiEvent per go/uievent. // The current aspect ratio of the PiP, logged when it changes. - PICTURE_IN_PICTURE_ASPECT_RATIO = 825; + PICTURE_IN_PICTURE_ASPECT_RATIO = 825 [deprecated=true]; // FIELD - length in dp of ACTION_LS_* gestures, or zero if not applicable // CATEGORY: GLOBAL_SYSTEM_UI diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 2cd4c6939fa9..b1340228ffa0 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -443,7 +443,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (reboundAService || configurationChanged) { onUserStateChangedLocked(userState); } - migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName); + // Passing 0 for restoreFromSdkInt to have this migration check execute each + // time. It can make sure a11y button settings are correctly if there's an a11y + // service updated and modifies the a11y button configuration. + migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName, + /* restoreFromSdkInt = */0); } } @@ -554,7 +558,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { restoreEnabledAccessibilityServicesLocked( intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE), - intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)); + intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE), + intent.getIntExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, + 0)); } } else if (ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED.equals(which)) { synchronized (mLock) { @@ -563,6 +569,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub intent.getIntExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, 0)); } + } else if (Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS.equals(which)) { + synchronized (mLock) { + restoreAccessibilityButtonTargetsLocked( + intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE), + intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)); + } } } } @@ -588,7 +600,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); final Set<String> targetsFromSetting = new ArraySet<>(); readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, - userState.mUserId, targetsFromSetting, str -> str); + userState.mUserId, str -> str, targetsFromSetting); final boolean targetsContainMagnification = targetsFromSetting.contains( MAGNIFICATION_CONTROLLER_NAME); if (targetsContainMagnification == displayMagnificationNavBarEnabled) { @@ -1189,7 +1201,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // the state since the context in which the current user // state was used has changed since it was inactive. onUserStateChangedLocked(userState); - migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null); + // It's better to have this migration in SettingsProvider. Unfortunately, + // SettingsProvider migrated database in a very early stage which A11yManagerService + // haven't finished or started the initialization. We cannot get enough information from + // A11yManagerService to execute these migrations in SettingsProvider. Passing 0 for + // restoreFromSdkInt to have this migration check execute every time, because we did not + // find out a way to detect the device finished the OTA and switch the user. + migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null, + /* restoreFromSdkInt = */0); if (announceNewUser) { // Schedule announcement of the current user if needed. @@ -1234,7 +1253,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // Called only during settings restore; currently supports only the owner user // TODO: http://b/22388012 - void restoreEnabledAccessibilityServicesLocked(String oldSetting, String newSetting) { + void restoreEnabledAccessibilityServicesLocked(String oldSetting, String newSetting, + int restoreFromSdkInt) { readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false); readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true); @@ -1246,7 +1266,32 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.mEnabledServices, UserHandle.USER_SYSTEM); onUserStateChangedLocked(userState); - migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null); + migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null, restoreFromSdkInt); + } + + /** + * User could enable accessibility services and configure accessibility button during the SUW. + * Merges current value of accessibility button settings into the restored one to make sure + * user's preferences of accessibility button updated in SUW are not lost. + * + * Called only during settings restore; currently supports only the owner user + * TODO: http://b/22388012 + */ + void restoreAccessibilityButtonTargetsLocked(String oldSetting, String newSetting) { + final Set<String> targetsFromSetting = new ArraySet<>(); + readColonDelimitedStringToSet(oldSetting, str -> str, targetsFromSetting, + /* doMerge = */false); + readColonDelimitedStringToSet(newSetting, str -> str, targetsFromSetting, + /* doMerge = */true); + + final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); + userState.mAccessibilityButtonTargets.clear(); + userState.mAccessibilityButtonTargets.addAll(targetsFromSetting); + persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, + UserHandle.USER_SYSTEM, userState.mAccessibilityButtonTargets, str -> str); + + scheduleNotifyClientsOfServicesStateChangeLocked(userState); + onUserStateChangedLocked(userState); } private int getClientStateLocked(AccessibilityUserState userState) { @@ -1569,8 +1614,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ private void readComponentNamesFromSettingLocked(String settingName, int userId, Set<ComponentName> outComponentNames) { - readColonDelimitedSettingToSet(settingName, userId, outComponentNames, - str -> ComponentName.unflattenFromString(str)); + readColonDelimitedSettingToSet(settingName, userId, + str -> ComponentName.unflattenFromString(str), outComponentNames); } /** @@ -1585,8 +1630,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void readComponentNamesFromStringLocked(String names, Set<ComponentName> outComponentNames, boolean doMerge) { - readColonDelimitedStringToSet(names, outComponentNames, doMerge, - str -> ComponentName.unflattenFromString(str)); + readColonDelimitedStringToSet(names, str -> ComponentName.unflattenFromString(str), + outComponentNames, doMerge); } @Override @@ -1596,15 +1641,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub componentName -> componentName.flattenToShortString()); } - private <T> void readColonDelimitedSettingToSet(String settingName, int userId, Set<T> outSet, - Function<String, T> toItem) { + private <T> void readColonDelimitedSettingToSet(String settingName, int userId, + Function<String, T> toItem, Set<T> outSet) { final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), settingName, userId); - readColonDelimitedStringToSet(settingValue, outSet, false, toItem); + readColonDelimitedStringToSet(settingValue, toItem, outSet, false); } - private <T> void readColonDelimitedStringToSet(String names, Set<T> outSet, boolean doMerge, - Function<String, T> toItem) { + private <T> void readColonDelimitedStringToSet(String names, Function<String, T> toItem, + Set<T> outSet, boolean doMerge) { if (!doMerge) { outSet.clear(); } @@ -2104,7 +2149,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId); final Set<String> targetsFromSetting = new ArraySet<>(); - readColonDelimitedStringToSet(settingValue, targetsFromSetting, false, str -> str); + readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false); // Fall back to device's default a11y service, only when setting is never updated. if (settingValue == null) { final String defaultService = mContext.getString( @@ -2128,7 +2173,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) { final Set<String> targetsFromSetting = new ArraySet<>(); readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, - userState.mUserId, targetsFromSetting, str -> str); + userState.mUserId, str -> str, targetsFromSetting); final Set<String> currentTargets = userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON); @@ -2392,9 +2437,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * (It happens when an enabled accessibility service package is upgraded.) * * @param packageName The package name to check, or {@code null} to check all services. + * @param restoreFromSdkInt The target sdk version of the restored source device, or {@code 0} + * if the caller is not related to the restore. */ private void migrateAccessibilityButtonSettingsIfNecessaryLocked( - AccessibilityUserState userState, @Nullable String packageName) { + AccessibilityUserState userState, @Nullable String packageName, int restoreFromSdkInt) { + // No need to migrate settings if they are restored from a version after Q. + if (restoreFromSdkInt > Build.VERSION_CODES.Q) { + return; + } final Set<String> buttonTargets = userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON); int lastSize = buttonTargets.size(); diff --git a/services/core/Android.bp b/services/core/Android.bp index 1093515ac525..e76ec743661c 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -79,6 +79,7 @@ java_library_static { ":framework_native_aidl", ":gsiservice_aidl", ":idmap2_aidl", + ":inputconstants_aidl", ":installd_aidl", ":storaged_aidl", ":vold_aidl", diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java index 7cf5fd621f18..b7fed87d570d 100644 --- a/services/core/java/android/os/BatteryStatsInternal.java +++ b/services/core/java/android/os/BatteryStatsInternal.java @@ -50,5 +50,5 @@ public abstract class BatteryStatsInternal { * Informs battery stats of binder stats for the given work source UID. */ public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount, - Collection<BinderCallsStats.CallStat> callStats); + Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids); } diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index df9dee89f5a2..6dbb1e922f60 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -137,6 +137,7 @@ final class UiModeManagerService extends SystemService { int mCurUiMode = 0; private int mSetUiMode = 0; private boolean mHoldingConfiguration = false; + private int mCurrentUser; private Configuration mConfiguration = new Configuration(); boolean mSystemReady; @@ -325,6 +326,7 @@ final class UiModeManagerService extends SystemService { @Override public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { + mCurrentUser = to.getUserIdentifier(); getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver); verifySetupWizardCompleted(); } @@ -727,16 +729,30 @@ final class UiModeManagerService extends SystemService { @Override public boolean setNightModeActivated(boolean active) { + if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) + != PackageManager.PERMISSION_GRANTED)) { + Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission"); + return false; + } + final int user = Binder.getCallingUserHandle().getIdentifier(); + if (user != mCurrentUser && getContext().checkCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS) + != PackageManager.PERMISSION_GRANTED) { + Slog.e(TAG, "Target user is not current user," + + " INTERACT_ACROSS_USERS permission is required"); + return false; + + } synchronized (mLock) { - final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) { unregisterScreenOffEventLocked(); mOverrideNightModeOff = !active; mOverrideNightModeOn = active; - mOverrideNightModeUser = user; - persistNightModeOverrides(user); + mOverrideNightModeUser = mCurrentUser; + persistNightModeOverrides(mCurrentUser); } else if (mNightMode == UiModeManager.MODE_NIGHT_NO && active) { mNightMode = UiModeManager.MODE_NIGHT_YES; @@ -746,7 +762,7 @@ final class UiModeManagerService extends SystemService { } updateConfigurationLocked(); applyConfigurationExternallyLocked(); - persistNightMode(user); + persistNightMode(mCurrentUser); return true; } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 72f29b431880..2534a535079e 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -76,6 +76,8 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; +import libcore.util.NativeAllocationRegistry; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -115,7 +117,6 @@ public class VibratorService extends IVibratorService.Stub // If HAL supports callbacks set the timeout to ASYNC_TIMEOUT_MULTIPLIER * duration. private static final long ASYNC_TIMEOUT_MULTIPLIER = 2; - // A mapping from the intensity adjustment to the scaling to apply, where the intensity // adjustment is defined as the delta between the default intensity level and the user selected // intensity level. It's important that we apply the scaling on the delta between the two so @@ -128,8 +129,6 @@ public class VibratorService extends IVibratorService.Stub private final LinkedList<VibrationInfo> mPreviousVibrations; private final int mPreviousVibrationsLimit; private final boolean mAllowPriorityVibrationsInLowPowerMode; - private final boolean mSupportsAmplitudeControl; - private final boolean mSupportsExternalControl; private final List<Integer> mSupportedEffects; private final long mCapabilities; private final int mDefaultVibrationAmplitude; @@ -174,22 +173,23 @@ public class VibratorService extends IVibratorService.Stub private int mRingIntensity; private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>(); - static native boolean vibratorExists(); - static native void vibratorInit(); + static native long vibratorInit(); + + static native long vibratorGetFinalizer(); + static native boolean vibratorExists(long controllerPtr); static native void vibratorOn(long milliseconds); - static native void vibratorOff(); - static native boolean vibratorSupportsAmplitudeControl(); - static native void vibratorSetAmplitude(int amplitude); - static native int[] vibratorGetSupportedEffects(); + static native void vibratorOff(long controllerPtr); + static native void vibratorSetAmplitude(long controllerPtr, int amplitude); + static native int[] vibratorGetSupportedEffects(long controllerPtr); static native long vibratorPerformEffect(long effect, long strength, Vibration vibration, boolean withCallback); static native void vibratorPerformComposedEffect( VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration); - static native boolean vibratorSupportsExternalControl(); - static native void vibratorSetExternalControl(boolean enabled); - static native long vibratorGetCapabilities(); - static native void vibratorAlwaysOnEnable(long id, long effect, long strength); - static native void vibratorAlwaysOnDisable(long id); + static native void vibratorSetExternalControl(long controllerPtr, boolean enabled); + static native long vibratorGetCapabilities(long controllerPtr); + static native void vibratorAlwaysOnEnable(long controllerPtr, long id, long effect, + long strength); + static native void vibratorAlwaysOnDisable(long controllerPtr, long id); private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, @@ -370,13 +370,20 @@ public class VibratorService extends IVibratorService.Stub mNativeWrapper = injector.getNativeWrapper(); mH = injector.createHandler(Looper.myLooper()); - mNativeWrapper.vibratorInit(); + long controllerPtr = mNativeWrapper.vibratorInit(); + long finalizerPtr = mNativeWrapper.vibratorGetFinalizer(); + + if (finalizerPtr != 0) { + NativeAllocationRegistry registry = + NativeAllocationRegistry.createMalloced( + VibratorService.class.getClassLoader(), finalizerPtr); + registry.registerNativeAllocation(this, controllerPtr); + } + // Reset the hardware to a default state, in case this is a runtime // restart instead of a fresh boot. mNativeWrapper.vibratorOff(); - mSupportsAmplitudeControl = mNativeWrapper.vibratorSupportsAmplitudeControl(); - mSupportsExternalControl = mNativeWrapper.vibratorSupportsExternalControl(); mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects()); mCapabilities = mNativeWrapper.vibratorGetCapabilities(); @@ -605,7 +612,8 @@ public class VibratorService extends IVibratorService.Stub synchronized (mInputDeviceVibrators) { // Input device vibrators don't support amplitude controls yet, but are still used over // the system vibrator when connected. - return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty(); + return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL) + && mInputDeviceVibrators.isEmpty(); } } @@ -1288,7 +1296,7 @@ public class VibratorService extends IVibratorService.Stub } private void doVibratorSetAmplitude(int amplitude) { - if (mSupportsAmplitudeControl) { + if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { mNativeWrapper.vibratorSetAmplitude(amplitude); } } @@ -1708,14 +1716,25 @@ public class VibratorService extends IVibratorService.Stub @VisibleForTesting public static class NativeWrapper { + private long mNativeControllerPtr = 0; + /** Checks if vibrator exists on device. */ public boolean vibratorExists() { - return VibratorService.vibratorExists(); + return VibratorService.vibratorExists(mNativeControllerPtr); } - /** Initializes connection to vibrator HAL service. */ - public void vibratorInit() { - VibratorService.vibratorInit(); + /** + * Returns native pointer to newly created controller and initializes connection to vibrator + * HAL service. + */ + public long vibratorInit() { + mNativeControllerPtr = VibratorService.vibratorInit(); + return mNativeControllerPtr; + } + + /** Returns pointer to native finalizer function to be called by GC. */ + public long vibratorGetFinalizer() { + return VibratorService.vibratorGetFinalizer(); } /** Turns vibrator on for given time. */ @@ -1725,22 +1744,17 @@ public class VibratorService extends IVibratorService.Stub /** Turns vibrator off. */ public void vibratorOff() { - VibratorService.vibratorOff(); - } - - /** Returns true if vibrator supports {@link #vibratorSetAmplitude(int)}. */ - public boolean vibratorSupportsAmplitudeControl() { - return VibratorService.vibratorSupportsAmplitudeControl(); + VibratorService.vibratorOff(mNativeControllerPtr); } /** Sets the amplitude for the vibrator to run. */ public void vibratorSetAmplitude(int amplitude) { - VibratorService.vibratorSetAmplitude(amplitude); + VibratorService.vibratorSetAmplitude(mNativeControllerPtr, amplitude); } /** Returns all predefined effects supported by the device vibrator. */ public int[] vibratorGetSupportedEffects() { - return VibratorService.vibratorGetSupportedEffects(); + return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr); } /** Turns vibrator on to perform one of the supported effects. */ @@ -1755,29 +1769,24 @@ public class VibratorService extends IVibratorService.Stub VibratorService.vibratorPerformComposedEffect(effect, vibration); } - /** Returns true if vibrator supports {@link #vibratorSetExternalControl(boolean)}. */ - public boolean vibratorSupportsExternalControl() { - return VibratorService.vibratorSupportsExternalControl(); - } - /** Enabled the device vibrator to be controlled by another service. */ public void vibratorSetExternalControl(boolean enabled) { - VibratorService.vibratorSetExternalControl(enabled); + VibratorService.vibratorSetExternalControl(mNativeControllerPtr, enabled); } /** Returns all capabilities of the device vibrator. */ public long vibratorGetCapabilities() { - return VibratorService.vibratorGetCapabilities(); + return VibratorService.vibratorGetCapabilities(mNativeControllerPtr); } /** Enable always-on vibration with given id and effect. */ public void vibratorAlwaysOnEnable(long id, long effect, long strength) { - VibratorService.vibratorAlwaysOnEnable(id, effect, strength); + VibratorService.vibratorAlwaysOnEnable(mNativeControllerPtr, id, effect, strength); } /** Disable always-on vibration for given id. */ public void vibratorAlwaysOnDisable(long id) { - VibratorService.vibratorAlwaysOnDisable(id); + VibratorService.vibratorAlwaysOnDisable(mNativeControllerPtr, id); } } @@ -1852,7 +1861,7 @@ public class VibratorService extends IVibratorService.Stub @Override public int onExternalVibrationStart(ExternalVibration vib) { - if (!mSupportsExternalControl) { + if (!hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { return SCALE_MUTE; } if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE, @@ -2142,10 +2151,10 @@ public class VibratorService extends IVibratorService.Stub if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { pw.println(" Compose effects"); } - if (mSupportsAmplitudeControl || hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { + if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { pw.println(" Amplitude control"); } - if (mSupportsExternalControl || hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { + if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { pw.println(" External control"); } if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c187772df6e4..91a1487c5245 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -41,6 +41,7 @@ import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.FactoryTest.FACTORY_TEST_OFF; +import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; @@ -130,10 +131,10 @@ import static com.android.server.wm.ActivityTaskManagerService.DUMP_LASTANR_TRAC import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_CMD; import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_SHORT_CMD; import static com.android.server.wm.ActivityTaskManagerService.DUMP_STARTER_CMD; -import static com.android.server.wm.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToString; + import android.Manifest; import android.Manifest.permission; import android.annotation.BroadcastBehavior; @@ -1955,7 +1956,7 @@ public class ActivityManagerService extends IActivityManager.Stub nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null); } memInfo.readMemInfo(); - synchronized (ActivityManagerService.this) { + synchronized (mProcessStats.mLock) { if (DEBUG_PSS) Slog.d(TAG_PSS, "Collected native and kernel memory in " + (SystemClock.uptimeMillis()-start) + "ms"); final long cachedKb = memInfo.getCachedSizeKb(); @@ -7047,9 +7048,7 @@ public class ActivityManagerService extends IActivityManager.Stub mUsageStatsService.prepareShutdown(); } mBatteryStatsService.shutdown(); - synchronized (this) { - mProcessStats.shutdownLocked(); - } + mProcessStats.shutdown(); return timedout; } @@ -12351,7 +12350,7 @@ public class ActivityManagerService extends IActivityManager.Stub MemInfoReader memInfo = new MemInfoReader(); memInfo.readMemInfo(); if (nativeProcTotalPss > 0) { - synchronized (this) { + synchronized (mProcessStats.mLock) { final long cachedKb = memInfo.getCachedSizeKb(); final long freeKb = memInfo.getFreeSizeKb(); final long zramKb = memInfo.getZramTotalSizeKb(); @@ -12933,7 +12932,7 @@ public class ActivityManagerService extends IActivityManager.Stub MemInfoReader memInfo = new MemInfoReader(); memInfo.readMemInfo(); if (nativeProcTotalPss > 0) { - synchronized (this) { + synchronized (mProcessStats.mLock) { final long cachedKb = memInfo.getCachedSizeKb(); final long freeKb = memInfo.getFreeSizeKb(); final long zramKb = memInfo.getZramTotalSizeKb(); @@ -16504,9 +16503,7 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override public void run() { - synchronized (mService) { - mProcessStats.writeStateAsyncLocked(); - } + mProcessStats.writeStateAsync(); } } @@ -16556,9 +16553,13 @@ public class ActivityManagerService extends IActivityManager.Stub } mLastMemoryLevel = memFactor; mLastNumProcesses = mProcessList.getLruSizeLocked(); - boolean allChanged = mProcessStats.setMemFactorLocked( - memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now); - final int trackerMemFactor = mProcessStats.getMemFactorLocked(); + boolean allChanged; + int trackerMemFactor; + synchronized (mProcessStats.mLock) { + allChanged = mProcessStats.setMemFactorLocked( + memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now); + trackerMemFactor = mProcessStats.getMemFactorLocked(); + } if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) { if (mLowRamStartTime == 0) { mLowRamStartTime = now; @@ -18353,19 +18354,20 @@ public class ActivityManagerService extends IActivityManager.Stub throw new SecurityException("Requires permission " + FILTER_EVENTS); } ProcessRecord proc; - long timeout; + long timeoutMillis; synchronized (this) { synchronized (mPidsSelfLocked) { proc = mPidsSelfLocked.get(pid); } - timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS; + timeoutMillis = proc != null ? proc.getInputDispatchingTimeoutMillis() : + DEFAULT_DISPATCHING_TIMEOUT_MILLIS; } if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) { - return -1; + return 0; } - return timeout; + return timeoutMillis; } /** diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 5081b360b4fe..d72998ba95f1 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -227,8 +227,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub @Override public void noteBinderCallStats(int workSourceUid, long incrementatCallCount, - Collection<BinderCallsStats.CallStat> callStats) { - mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats); + Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) { + mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats, + binderThreadNativeTids); } } diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 8970ec4c7bb7..0f2dfcc699e2 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -116,6 +116,10 @@ final class CoreSettingsObserver extends ContentObserver { WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, boolean.class, WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT)); sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>( + DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, + WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, int.class, + WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT)); + sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>( DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.FINGER_TO_CURSOR_DISTANCE, WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE, int.class, WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT)); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index f0343e1d807c..bf15f1737cfc 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -659,13 +659,15 @@ public final class OomAdjuster { updateUidsLocked(activeUids, nowElapsed); - if (mService.mProcessStats.shouldWriteNowLocked(now)) { - mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService, - mService.mProcessStats)); - } + synchronized (mService.mProcessStats.mLock) { + if (mService.mProcessStats.shouldWriteNowLocked(now)) { + mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService, + mService.mProcessStats)); + } - // Run this after making sure all procstates are updated. - mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now); + // Run this after making sure all procstates are updated. + mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now); + } if (DEBUG_OOM_ADJ) { final long duration = SystemClock.uptimeMillis() - now; diff --git a/services/core/java/com/android/server/am/PendingTempWhitelists.java b/services/core/java/com/android/server/am/PendingTempWhitelists.java index b36e3c7b9e35..50d58f02baa7 100644 --- a/services/core/java/com/android/server/am/PendingTempWhitelists.java +++ b/services/core/java/com/android/server/am/PendingTempWhitelists.java @@ -32,13 +32,13 @@ final class PendingTempWhitelists { void put(int uid, ActivityManagerService.PendingTempWhitelist value) { mPendingTempWhitelist.put(uid, value); - mService.mAtmInternal.onUidAddedToPendingTempWhitelist(uid, value.tag); + mService.mAtmInternal.onUidAddedToPendingTempAllowlist(uid, value.tag); } void removeAt(int index) { final int uid = mPendingTempWhitelist.keyAt(index); mPendingTempWhitelist.removeAt(index); - mService.mAtmInternal.onUidRemovedFromPendingTempWhitelist(uid); + mService.mAtmInternal.onUidRemovedFromPendingTempAllowlist(uid); } ActivityManagerService.PendingTempWhitelist get(int uid) { diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index cd4302bb42fa..e3e13391a8b0 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -673,30 +673,33 @@ class ProcessRecord implements WindowProcessListener { public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) { if (thread == null) { - final ProcessState origBase = baseProcessTracker; - if (origBase != null) { - origBase.setState(ProcessStats.STATE_NOTHING, - tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList); - for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { - FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, - uid, processName, pkgList.keyAt(ipkg), - ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), - pkgList.valueAt(ipkg).appVersion); - } - origBase.makeInactive(); - } - baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid, - info.longVersionCode, processName); - baseProcessTracker.makeActive(); - for (int i=0; i<pkgList.size(); i++) { - ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); - if (holder.state != null && holder.state != origBase) { - holder.state.makeInactive(); + synchronized (tracker.mLock) { + final ProcessState origBase = baseProcessTracker; + if (origBase != null) { + origBase.setState(ProcessStats.STATE_NOTHING, + tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), + pkgList.mPkgList); + for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { + FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, + uid, processName, pkgList.keyAt(ipkg), + ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), + pkgList.valueAt(ipkg).appVersion); + } + origBase.makeInactive(); } - tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid, + baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid, info.longVersionCode, processName); - if (holder.state != baseProcessTracker) { - holder.state.makeActive(); + baseProcessTracker.makeActive(); + for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) { + ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); + if (holder.state != null && holder.state != origBase) { + holder.state.makeInactive(); + } + tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid, + info.longVersionCode, processName); + if (holder.state != baseProcessTracker) { + holder.state.makeActive(); + } } } } @@ -707,27 +710,30 @@ class ProcessRecord implements WindowProcessListener { public void makeInactive(ProcessStatsService tracker) { thread = null; mWindowProcessController.setThread(null); - final ProcessState origBase = baseProcessTracker; - if (origBase != null) { + synchronized (tracker.mLock) { + final ProcessState origBase = baseProcessTracker; if (origBase != null) { - origBase.setState(ProcessStats.STATE_NOTHING, - tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList); - for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { - FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, - uid, processName, pkgList.keyAt(ipkg), - ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), - pkgList.valueAt(ipkg).appVersion); + if (origBase != null) { + origBase.setState(ProcessStats.STATE_NOTHING, + tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), + pkgList.mPkgList); + for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { + FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, + uid, processName, pkgList.keyAt(ipkg), + ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), + pkgList.valueAt(ipkg).appVersion); + } + origBase.makeInactive(); } - origBase.makeInactive(); - } - baseProcessTracker = null; - for (int i=0; i<pkgList.size(); i++) { - ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); - if (holder.state != null && holder.state != origBase) { - holder.state.makeInactive(); + baseProcessTracker = null; + for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) { + ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); + if (holder.state != null && holder.state != origBase) { + holder.state.makeInactive(); + } + holder.pkg = null; + holder.state = null; } - holder.pkg = null; - holder.state = null; } } } @@ -1026,17 +1032,19 @@ class ProcessRecord implements WindowProcessListener { */ public boolean addPackage(String pkg, long versionCode, ProcessStatsService tracker) { if (!pkgList.containsKey(pkg)) { - ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( - versionCode); - if (baseProcessTracker != null) { - tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode, - processName); - pkgList.put(pkg, holder); - if (holder.state != baseProcessTracker) { - holder.state.makeActive(); + synchronized (tracker.mLock) { + ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( + versionCode); + if (baseProcessTracker != null) { + tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode, + processName); + pkgList.put(pkg, holder); + if (holder.state != baseProcessTracker) { + holder.state.makeActive(); + } + } else { + pkgList.put(pkg, holder); } - } else { - pkgList.put(pkg, holder); } return true; } @@ -1072,31 +1080,33 @@ class ProcessRecord implements WindowProcessListener { public void resetPackageList(ProcessStatsService tracker) { final int N = pkgList.size(); if (baseProcessTracker != null) { - long now = SystemClock.uptimeMillis(); - baseProcessTracker.setState(ProcessStats.STATE_NOTHING, - tracker.getMemFactorLocked(), now, pkgList.mPkgList); - for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { - FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, - uid, processName, pkgList.keyAt(ipkg), - ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), - pkgList.valueAt(ipkg).appVersion); - } - if (N != 1) { - for (int i=0; i<N; i++) { - ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); - if (holder.state != null && holder.state != baseProcessTracker) { - holder.state.makeInactive(); - } - + synchronized (tracker.mLock) { + long now = SystemClock.uptimeMillis(); + baseProcessTracker.setState(ProcessStats.STATE_NOTHING, + tracker.getMemFactorLocked(), now, pkgList.mPkgList); + for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { + FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, + uid, processName, pkgList.keyAt(ipkg), + ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), + pkgList.valueAt(ipkg).appVersion); } - pkgList.clear(); - ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( - info.longVersionCode); - tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid, - info.longVersionCode, processName); - pkgList.put(info.packageName, holder); - if (holder.state != baseProcessTracker) { - holder.state.makeActive(); + if (N != 1) { + for (int i = 0; i < N; i++) { + ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); + if (holder.state != null && holder.state != baseProcessTracker) { + holder.state.makeInactive(); + } + + } + pkgList.clear(); + ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( + info.longVersionCode); + tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid, + info.longVersionCode, processName); + pkgList.put(info.packageName, holder); + if (holder.state != baseProcessTracker) { + holder.state.makeActive(); + } } } } else if (N != 1) { @@ -1522,8 +1532,8 @@ class ProcessRecord implements WindowProcessListener { } } - public long getInputDispatchingTimeout() { - return mWindowProcessController.getInputDispatchingTimeout(); + public long getInputDispatchingTimeoutMillis() { + return mWindowProcessController.getInputDispatchingTimeoutMillis(); } public int getProcessClassEnum() { diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java index a168af5ad842..4e8c386a3c66 100644 --- a/services/core/java/com/android/server/am/ProcessStatsService.java +++ b/services/core/java/com/android/server/am/ProcessStatsService.java @@ -71,33 +71,62 @@ public final class ProcessStatsService extends IProcessStats.Stub { final ActivityManagerService mAm; final File mBaseDir; - ProcessStats mProcessStats; + + // Note: The locking order of the below 3 locks should be: + // mLock, mPendingWriteLock, mFileLock + + // The lock object to protect the internal state/structures + final Object mLock = new Object(); + + // The lock object to protect the access to pending writes + final Object mPendingWriteLock = new Object(); + + // The lock object to protect the access to all of the file read/write + final ReentrantLock mFileLock = new ReentrantLock(); + + @GuardedBy("mLock") + final ProcessStats mProcessStats; + + @GuardedBy("mFileLock") AtomicFile mFile; + + @GuardedBy("mLock") boolean mCommitPending; + + @GuardedBy("mLock") boolean mShuttingDown; + + @GuardedBy("mLock") int mLastMemOnlyState = -1; boolean mMemFactorLowered; - final ReentrantLock mWriteLock = new ReentrantLock(); - final Object mPendingWriteLock = new Object(); + @GuardedBy("mPendingWriteLock") AtomicFile mPendingWriteFile; + + @GuardedBy("mPendingWriteLock") Parcel mPendingWrite; + + @GuardedBy("mPendingWriteLock") boolean mPendingWriteCommitted; + + @GuardedBy("mLock") long mLastWriteTime; /** For CTS to inject the screen state. */ - @GuardedBy("mAm") + @GuardedBy("mLock") Boolean mInjectedScreenState; public ProcessStatsService(ActivityManagerService am, File file) { mAm = am; mBaseDir = file; mBaseDir.mkdirs(); - mProcessStats = new ProcessStats(true); - updateFile(); + synchronized (mLock) { + mProcessStats = new ProcessStats(true); + updateFileLocked(); + } SystemProperties.addChangeCallback(new Runnable() { @Override public void run() { - synchronized (mAm) { + synchronized (mLock) { if (mProcessStats.evaluateSystemProperties(false)) { mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS; writeStateLocked(true, true); @@ -121,32 +150,33 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } - @GuardedBy("mAm") - public void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder, + @GuardedBy("mLock") + void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder, String packageName, int uid, long versionCode, String processName) { holder.pkg = mProcessStats.getPackageStateLocked(packageName, uid, versionCode); holder.state = mProcessStats.getProcessStateLocked(holder.pkg, processName); } - @GuardedBy("mAm") - public ProcessState getProcessStateLocked(String packageName, + @GuardedBy("mLock") + ProcessState getProcessStateLocked(String packageName, int uid, long versionCode, String processName) { return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName); } - @GuardedBy("mAm") - public ServiceState getServiceStateLocked(String packageName, int uid, + ServiceState getServiceState(String packageName, int uid, long versionCode, String processName, String className) { - return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName, - className); + synchronized (mLock) { + return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName, + className); + } } - public boolean isMemFactorLowered() { + boolean isMemFactorLowered() { return mMemFactorLowered; } - @GuardedBy("mAm") - public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) { + @GuardedBy("mLock") + boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) { mMemFactorLowered = memFactor < mLastMemOnlyState; mLastMemOnlyState = memFactor; if (mInjectedScreenState != null) { @@ -184,24 +214,24 @@ public final class ProcessStatsService extends IProcessStats.Stub { return false; } - @GuardedBy("mAm") - public int getMemFactorLocked() { + @GuardedBy("mLock") + int getMemFactorLocked() { return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0; } - @GuardedBy("mAm") - public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem, + @GuardedBy("mLock") + void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem, long nativeMem) { mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem); } - @GuardedBy("mAm") - public void updateTrackingAssociationsLocked(int curSeq, long now) { + @GuardedBy("mLock") + void updateTrackingAssociationsLocked(int curSeq, long now) { mProcessStats.updateTrackingAssociationsLocked(curSeq, now); } - @GuardedBy("mAm") - public boolean shouldWriteNowLocked(long now) { + @GuardedBy("mLock") + boolean shouldWriteNowLocked(long now) { if (now > (mLastWriteTime+WRITE_PERIOD)) { if (SystemClock.elapsedRealtime() > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD) && @@ -214,25 +244,27 @@ public final class ProcessStatsService extends IProcessStats.Stub { return false; } - @GuardedBy("mAm") - public void shutdownLocked() { + void shutdown() { Slog.w(TAG, "Writing process stats before shutdown..."); - mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN; - writeStateSyncLocked(); - mShuttingDown = true; + synchronized (mLock) { + mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN; + writeStateSyncLocked(); + mShuttingDown = true; + } } - @GuardedBy("mAm") - public void writeStateAsyncLocked() { - writeStateLocked(false); + void writeStateAsync() { + synchronized (mLock) { + writeStateLocked(false); + } } - @GuardedBy("mAm") - public void writeStateSyncLocked() { + @GuardedBy("mLock") + private void writeStateSyncLocked() { writeStateLocked(true); } - @GuardedBy("mAm") + @GuardedBy("mLock") private void writeStateLocked(boolean sync) { if (mShuttingDown) { return; @@ -242,8 +274,8 @@ public final class ProcessStatsService extends IProcessStats.Stub { writeStateLocked(sync, commitPending); } - @GuardedBy("mAm") - public void writeStateLocked(boolean sync, final boolean commit) { + @GuardedBy("mLock") + private void writeStateLocked(boolean sync, final boolean commit) { final long totalTime; synchronized (mPendingWriteLock) { final long now = SystemClock.uptimeMillis(); @@ -255,13 +287,13 @@ public final class ProcessStatsService extends IProcessStats.Stub { mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; } mProcessStats.writeToParcel(mPendingWrite, 0); - mPendingWriteFile = new AtomicFile(mFile.getBaseFile()); + mPendingWriteFile = new AtomicFile(getCurrentFile()); mPendingWriteCommitted = commit; } if (commit) { mProcessStats.resetSafely(); - updateFile(); - mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); + updateFileLocked(); + scheduleRequestPssAllProcs(true, false); } mLastWriteTime = SystemClock.uptimeMillis(); totalTime = SystemClock.uptimeMillis() - now; @@ -279,14 +311,37 @@ public final class ProcessStatsService extends IProcessStats.Stub { performWriteState(totalTime); } - private void updateFile() { - mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX - + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX)); + private void scheduleRequestPssAllProcs(boolean always, boolean memLowered) { + mAm.mHandler.post(() -> { + synchronized (mAm) { + mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), always, memLowered); + } + }); + } + + @GuardedBy("mLock") + private void updateFileLocked() { + mFileLock.lock(); + try { + mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX + + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX)); + } finally { + mFileLock.unlock(); + } mLastWriteTime = SystemClock.uptimeMillis(); } - void performWriteState(long initialTime) { - if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile()); + private File getCurrentFile() { + mFileLock.lock(); + try { + return mFile.getBaseFile(); + } finally { + mFileLock.unlock(); + } + } + + private void performWriteState(long initialTime) { + if (DEBUG) Slog.d(TAG, "Performing write to " + getCurrentFile()); Parcel data; AtomicFile file; synchronized (mPendingWriteLock) { @@ -298,7 +353,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { } mPendingWrite = null; mPendingWriteFile = null; - mWriteLock.lock(); + mFileLock.lock(); } final long startTime = SystemClock.uptimeMillis(); @@ -316,13 +371,13 @@ public final class ProcessStatsService extends IProcessStats.Stub { file.failWrite(stream); } finally { data.recycle(); - trimHistoricStatesWriteLocked(); - mWriteLock.unlock(); + trimHistoricStatesWriteLF(); + mFileLock.unlock(); } } - @GuardedBy("mAm") - boolean readLocked(ProcessStats stats, AtomicFile file) { + @GuardedBy("mFileLock") + private boolean readLF(ProcessStats stats, AtomicFile file) { try { FileInputStream stream = file.openRead(); stats.read(stream); @@ -387,7 +442,8 @@ public final class ProcessStatsService extends IProcessStats.Stub { return true; } - private ArrayList<String> getCommittedFiles(int minNum, boolean inclCurrent, + @GuardedBy("mFileLock") + private ArrayList<String> getCommittedFilesLF(int minNum, boolean inclCurrent, boolean inclCheckedIn) { File[] files = mBaseDir.listFiles(); if (files == null || files.length <= minNum) { @@ -414,9 +470,9 @@ public final class ProcessStatsService extends IProcessStats.Stub { return filesArray; } - @GuardedBy("mAm") - public void trimHistoricStatesWriteLocked() { - ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true); + @GuardedBy("mFileLock") + private void trimHistoricStatesWriteLF() { + ArrayList<String> filesArray = getCommittedFilesLF(MAX_HISTORIC_STATES, false, true); if (filesArray == null) { return; } @@ -427,8 +483,8 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } - @GuardedBy("mAm") - boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, + @GuardedBy("mLock") + private boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, boolean sepProcStates, int[] procStates, long now, String reqPackage) { ArrayList<ProcessState> procs = mProcessStats.collectProcessesLocked( @@ -502,20 +558,21 @@ public final class ProcessStatsService extends IProcessStats.Stub { return res; } + @Override public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) { mAm.mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); Parcel current = Parcel.obtain(); - synchronized (mAm) { + synchronized (mLock) { long now = SystemClock.uptimeMillis(); mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); mProcessStats.mTimePeriodEndUptime = now; mProcessStats.writeToParcel(current, now, 0); } - mWriteLock.lock(); + mFileLock.lock(); try { if (historic != null) { - ArrayList<String> files = getCommittedFiles(0, false, true); + ArrayList<String> files = getCommittedFilesLF(0, false, true); if (files != null) { for (int i=files.size()-1; i>=0; i--) { try { @@ -529,7 +586,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } } finally { - mWriteLock.unlock(); + mFileLock.unlock(); } return current.marshall(); } @@ -563,9 +620,9 @@ public final class ProcessStatsService extends IProcessStats.Stub { android.Manifest.permission.PACKAGE_USAGE_STATS, null); long newHighWaterMark = highWaterMarkMs; - mWriteLock.lock(); + mFileLock.lock(); try { - ArrayList<String> files = getCommittedFiles(0, false, true); + ArrayList<String> files = getCommittedFilesLF(0, false, true); if (files != null) { String highWaterMarkStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss", highWaterMarkMs).toString(); @@ -612,7 +669,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { } catch (IOException e) { Slog.w(TAG, "Failure opening procstat file", e); } finally { - mWriteLock.unlock(); + mFileLock.unlock(); } return newHighWaterMark; } @@ -625,7 +682,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { return mAm.mConstants.MIN_ASSOC_LOG_DURATION; } - private ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section) + private static ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section) throws IOException { final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); Thread thr = new Thread("ProcessStats pipe output") { @@ -645,12 +702,13 @@ public final class ProcessStatsService extends IProcessStats.Stub { return fds[0]; } + @Override public ParcelFileDescriptor getStatsOverTime(long minTime) { mAm.mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); Parcel current = Parcel.obtain(); long curTime; - synchronized (mAm) { + synchronized (mLock) { long now = SystemClock.uptimeMillis(); mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); mProcessStats.mTimePeriodEndUptime = now; @@ -658,11 +716,11 @@ public final class ProcessStatsService extends IProcessStats.Stub { curTime = mProcessStats.mTimePeriodEndRealtime - mProcessStats.mTimePeriodStartRealtime; } - mWriteLock.lock(); + mFileLock.lock(); try { if (curTime < minTime) { // Need to add in older stats to reach desired time. - ArrayList<String> files = getCommittedFiles(0, false, true); + ArrayList<String> files = getCommittedFilesLF(0, false, true); if (files != null && files.size() > 0) { current.setDataPosition(0); ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current); @@ -673,7 +731,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { AtomicFile file = new AtomicFile(new File(files.get(i))); i--; ProcessStats moreStats = new ProcessStats(false); - readLocked(moreStats, file); + readLF(moreStats, file); if (moreStats.mReadError == null) { stats.add(moreStats); StringBuilder sb = new StringBuilder(); @@ -712,13 +770,14 @@ public final class ProcessStatsService extends IProcessStats.Stub { } catch (IOException e) { Slog.w(TAG, "Failed building output pipe", e); } finally { - mWriteLock.unlock(); + mFileLock.unlock(); } return null; } + @Override public int getCurrentMemoryState() { - synchronized (mAm) { + synchronized (mLock) { return mLastMemOnlyState; } } @@ -947,7 +1006,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { } else if ("--current".equals(arg)) { currentOnly = true; } else if ("--commit".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; writeStateLocked(true, true); pw.println("Process stats committed."); @@ -962,29 +1021,39 @@ public final class ProcessStatsService extends IProcessStats.Stub { } section = parseSectionOptions(args[i]); } else if ("--clear".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { mProcessStats.resetSafely(); - mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); - ArrayList<String> files = getCommittedFiles(0, true, true); - if (files != null) { - for (int fi=0; fi<files.size(); fi++) { - (new File(files.get(fi))).delete(); + scheduleRequestPssAllProcs(true, false); + mFileLock.lock(); + try { + ArrayList<String> files = getCommittedFilesLF(0, true, true); + if (files != null) { + for (int fi = files.size() - 1; fi >= 0; fi--) { + (new File(files.get(fi))).delete(); + } } + } finally { + mFileLock.unlock(); } pw.println("All process stats cleared."); quit = true; } } else if ("--write".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { writeStateSyncLocked(); pw.println("Process stats written."); quit = true; } } else if ("--read".equals(arg)) { - synchronized (mAm) { - readLocked(mProcessStats, mFile); - pw.println("Process stats read."); - quit = true; + synchronized (mLock) { + mFileLock.lock(); + try { + readLF(mProcessStats, mFile); + pw.println("Process stats read."); + quit = true; + } finally { + mFileLock.unlock(); + } } } else if ("--start-testing".equals(arg)) { synchronized (mAm) { @@ -999,17 +1068,17 @@ public final class ProcessStatsService extends IProcessStats.Stub { quit = true; } } else if ("--pretend-screen-on".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { mInjectedScreenState = true; } quit = true; } else if ("--pretend-screen-off".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { mInjectedScreenState = false; } quit = true; } else if ("--stop-pretend-screen".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { mInjectedScreenState = null; } quit = true; @@ -1060,7 +1129,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } pw.println(); - synchronized (mAm) { + synchronized (mLock) { dumpFilteredProcessesCsvLocked(pw, null, csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats, csvSepProcStats, csvProcStats, now, reqPackage); @@ -1090,14 +1159,26 @@ public final class ProcessStatsService extends IProcessStats.Stub { return; } else if (lastIndex > 0) { pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":"); - ArrayList<String> files = getCommittedFiles(0, false, true); - if (lastIndex >= files.size()) { - pw.print("Only have "); pw.print(files.size()); pw.println(" data sets"); - return; + + ArrayList<String> files; + AtomicFile file; + ProcessStats processStats; + + mFileLock.lock(); + try { + files = getCommittedFilesLF(0, false, true); + if (lastIndex >= files.size()) { + pw.print("Only have "); pw.print(files.size()); pw.println(" data sets"); + return; + } + file = new AtomicFile(new File(files.get(lastIndex))); + processStats = new ProcessStats(false); + readLF(processStats, file); + } finally { + mFileLock.unlock(); } - AtomicFile file = new AtomicFile(new File(files.get(lastIndex))); - ProcessStats processStats = new ProcessStats(false); - readLocked(processStats, file); + + // No lock is needed now, since only us have the access to the 'processStats'. if (processStats.mReadError != null) { if (isCheckin || isCompact) pw.print("err,"); pw.print("Failure reading "); pw.print(files.get(lastIndex)); @@ -1118,7 +1199,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, dumpAll, activeOnly, section); if (dumpAll) { - pw.print(" mFile="); pw.println(mFile.getBaseFile()); + pw.print(" mFile="); pw.println(getCurrentFile()); } } else { processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); @@ -1129,9 +1210,9 @@ public final class ProcessStatsService extends IProcessStats.Stub { boolean sepNeeded = false; if ((dumpAll || isCheckin) && !currentOnly) { - mWriteLock.lock(); + mFileLock.lock(); try { - ArrayList<String> files = getCommittedFiles(0, false, !isCheckin); + ArrayList<String> files = getCommittedFilesLF(0, false, !isCheckin); if (files != null) { int start = isCheckin ? 0 : (files.size() - maxNum); if (start < 0) { @@ -1142,7 +1223,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { try { AtomicFile file = new AtomicFile(new File(files.get(i))); ProcessStats processStats = new ProcessStats(false); - readLocked(processStats, file); + readLF(processStats, file); if (processStats.mReadError != null) { if (isCheckin || isCompact) pw.print("err,"); pw.print("Failure reading "); pw.print(files.get(i)); @@ -1188,11 +1269,11 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } } finally { - mWriteLock.unlock(); + mFileLock.unlock(); } } if (!isCheckin) { - synchronized (mAm) { + synchronized (mLock) { if (isCompact) { mProcessStats.dumpCheckinLocked(pw, reqPackage, section); } else { @@ -1204,7 +1285,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, dumpAll, activeOnly, section); if (dumpAll) { - pw.print(" mFile="); pw.println(mFile.getBaseFile()); + pw.print(" mFile="); pw.println(getCurrentFile()); } } else { mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); @@ -1249,7 +1330,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { // dump current procstats long now; - synchronized (mAm) { + synchronized (mLock) { now = SystemClock.uptimeMillis(); final long token = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW); mProcessStats.dumpDebug(proto, now, ProcessStats.REPORT_ALL); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 022b04d89774..4a2703056871 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -528,8 +528,9 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN return tracker; } if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) { - tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName, - serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.longVersionCode, + tracker = ams.mProcessStats.getServiceState(serviceInfo.packageName, + serviceInfo.applicationInfo.uid, + serviceInfo.applicationInfo.longVersionCode, serviceInfo.processName, serviceInfo.name); tracker.applyNewOwner(this); } @@ -546,7 +547,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN public void makeRestarting(int memFactor, long now) { if (restartTracker == null) { if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) { - restartTracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName, + restartTracker = ams.mProcessStats.getServiceState( + serviceInfo.packageName, serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.longVersionCode, serviceInfo.processName, serviceInfo.name); diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index e6480fc6cde8..ee441bf06d04 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -1889,6 +1889,7 @@ public class AppOpsService extends IAppOpsService.Stub { synchronized (this) { if (mWriteScheduled) { mWriteScheduled = false; + mHandler.removeCallbacks(mWriteRunner); doWrite = true; } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 45f95fd3f663..2bbbbf1664d1 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -224,12 +224,35 @@ import java.util.concurrent.atomic.AtomicBoolean; if (!addSpeakerphoneClient(cb, pid, on)) { return false; } + if (on) { + // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes + // are mutually exclusive. + // See symmetrical operation for startBluetoothScoForClient_Sync(). + mBtHelper.stopBluetoothScoForPid(pid); + } final boolean wasOn = isSpeakerphoneOn(); updateSpeakerphoneOn(eventSource); return (wasOn != isSpeakerphoneOn()); } } + /** + * Turns speakerphone off for a given pid and update speakerphone state. + * @param pid + */ + @GuardedBy("mDeviceStateLock") + private void setSpeakerphoneOffForPid(int pid) { + SpeakerphoneClient client = getSpeakerphoneClientForPid(pid); + if (client == null) { + return; + } + client.unregisterDeathRecipient(); + mSpeakerphoneClients.remove(client); + final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(") + .append(pid).append(")").toString(); + updateSpeakerphoneOn(eventSource); + } + @GuardedBy("mDeviceStateLock") private void updateSpeakerphoneOn(String eventSource) { if (isSpeakerphoneOnRequested()) { @@ -488,6 +511,10 @@ import java.util.concurrent.atomic.AtomicBoolean; /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode, @NonNull String eventSource) { synchronized (mDeviceStateLock) { + // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes + // are mutually exclusive. + // See symmetrical operation for setSpeakerphoneOn(true). + setSpeakerphoneOffForPid(Binder.getCallingPid()); mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); } } @@ -1379,6 +1406,16 @@ import java.util.concurrent.atomic.AtomicBoolean; return false; } + @GuardedBy("mDeviceStateLock") + private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) { + for (SpeakerphoneClient cl : mSpeakerphoneClients) { + if (cl.getPid() == pid) { + return cl; + } + } + return null; + } + // List of clients requesting speakerPhone ON @GuardedBy("mDeviceStateLock") private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients = diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 32be03fb4ef8..366f30318cd5 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -414,13 +414,6 @@ public class AudioService extends IAudioService.Stub AppOpsManager.OP_AUDIO_MEDIA_VOLUME // STREAM_ASSISTANT }; - private static Set<Integer> sDeviceVolumeBehaviorSupportedDeviceOutSet = new HashSet<>( - Arrays.asList( - AudioSystem.DEVICE_OUT_HDMI, - AudioSystem.DEVICE_OUT_HDMI_ARC, - AudioSystem.DEVICE_OUT_SPDIF, - AudioSystem.DEVICE_OUT_LINE)); - private final boolean mUseFixedVolume; // If absolute volume is supported in AVRCP device @@ -4952,11 +4945,6 @@ public class AudioService extends IAudioService.Stub private void setDeviceVolumeBehaviorInternal(int audioSystemDeviceOut, @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @NonNull String caller) { - if (!sDeviceVolumeBehaviorSupportedDeviceOutSet.contains(audioSystemDeviceOut)) { - // unsupported for now - throw new IllegalArgumentException("Unsupported device type " + audioSystemDeviceOut); - } - // update device masks based on volume behavior switch (deviceVolumeBehavior) { case AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE: @@ -4990,20 +4978,14 @@ public class AudioService extends IAudioService.Stub * @param device the audio output device type * @return the volume behavior for the device */ - public @AudioManager.DeviceVolumeBehaviorState int getDeviceVolumeBehavior( - @NonNull AudioDeviceAttributes device) { + public @AudioManager.DeviceVolumeBehavior + int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) { // verify permissions enforceModifyAudioRoutingPermission(); // translate Java device type to native device type (for the devices masks for full / fixed) final int audioSystemDeviceOut = AudioDeviceInfo.convertDeviceTypeToInternalDevice( device.getType()); - if (!sDeviceVolumeBehaviorSupportedDeviceOutSet.contains(audioSystemDeviceOut) - && audioSystemDeviceOut != AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP - && audioSystemDeviceOut != AudioSystem.DEVICE_OUT_HEARING_AID) { - throw new IllegalArgumentException("Unsupported volume behavior " - + audioSystemDeviceOut); - } int setDeviceVolumeBehavior = retrieveStoredDeviceVolumeBehavior(audioSystemDeviceOut); if (setDeviceVolumeBehavior != AudioManager.DEVICE_VOLUME_BEHAVIOR_UNSET) { @@ -9081,7 +9063,7 @@ public class AudioService extends IAudioService.Stub } private void restoreDeviceVolumeBehavior() { - for (int deviceType : sDeviceVolumeBehaviorSupportedDeviceOutSet) { + for (int deviceType : AudioSystem.DEVICE_OUT_ALL_SET) { if (DEBUG_VOL) { Log.d(TAG, "Retrieving Volume Behavior for DeviceType: " + deviceType); } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 5e8f1ef0db85..ded0f9a3dca7 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -432,19 +432,35 @@ public class BtHelper { // and this must be done on behalf of system server to make sure permissions are granted. final long ident = Binder.clearCallingIdentity(); if (client != null) { - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); - client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, - SCO_MODE_VIRTUAL_CALL); - // If a disconnection is pending, the client will be removed whne clearAllScoClients() - // is called form receiveBtEvent() - if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ - && mScoAudioState != SCO_STATE_DEACTIVATING) { - client.remove(false /*stop */, true /*unregister*/); - } + stopAndRemoveClient(client, eventSource); } Binder.restoreCallingIdentity(ident); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + /*package*/ synchronized void stopBluetoothScoForPid(int pid) { + ScoClient client = getScoClientForPid(pid); + if (client == null) { + return; + } + final String eventSource = new StringBuilder("stopBluetoothScoForPid(") + .append(pid).append(")").toString(); + stopAndRemoveClient(client, eventSource); + } + + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) { + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); + client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, + SCO_MODE_VIRTUAL_CALL); + // If a disconnection is pending, the client will be removed when clearAllScoClients() + // is called form receiveBtEvent() + if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ + && mScoAudioState != SCO_STATE_DEACTIVATING) { + client.remove(false /*stop */, true /*unregister*/); + } + } /*package*/ synchronized void setHearingAidVolume(int index, int streamType) { if (mHearingAid == null) { @@ -982,6 +998,16 @@ public class BtHelper { return null; } + @GuardedBy("BtHelper.this") + private ScoClient getScoClientForPid(int pid) { + for (ScoClient cl : mScoClients) { + if (cl.getPid() == pid) { + return cl; + } + } + return null; + } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 1f85d1046523..1c93d4eb599b 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -48,6 +48,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.net.ConnectivityManager; +import android.net.DnsResolver; import android.net.INetworkManagementEventObserver; import android.net.Ikev2VpnProfile; import android.net.IpPrefix; @@ -79,6 +80,7 @@ import android.net.ipsec.ike.IkeSessionParams; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.os.CancellationSignal; import android.os.FileUtils; import android.os.IBinder; import android.os.INetworkManagementService; @@ -123,6 +125,7 @@ import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.ArrayList; @@ -134,6 +137,8 @@ import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -190,6 +195,7 @@ public class Vpn { // automated reconnection private final Context mContext; + @VisibleForTesting final Dependencies mDeps; private final NetworkInfo mNetworkInfo; @VisibleForTesting protected String mPackage; private int mOwnerUID; @@ -252,17 +258,143 @@ public class Vpn { // Handle of the user initiating VPN. private final int mUserHandle; + interface RetryScheduler { + void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException; + } + + static class Dependencies { + public void startService(final String serviceName) { + SystemService.start(serviceName); + } + + public void stopService(final String serviceName) { + SystemService.stop(serviceName); + } + + public boolean isServiceRunning(final String serviceName) { + return SystemService.isRunning(serviceName); + } + + public boolean isServiceStopped(final String serviceName) { + return SystemService.isStopped(serviceName); + } + + public File getStateFile() { + return new File("/data/misc/vpn/state"); + } + + public void sendArgumentsToDaemon( + final String daemon, final LocalSocket socket, final String[] arguments, + final RetryScheduler retryScheduler) throws IOException, InterruptedException { + final LocalSocketAddress address = new LocalSocketAddress( + daemon, LocalSocketAddress.Namespace.RESERVED); + + // Wait for the socket to connect. + while (true) { + try { + socket.connect(address); + break; + } catch (Exception e) { + // ignore + } + retryScheduler.checkInterruptAndDelay(true /* sleepLonger */); + } + socket.setSoTimeout(500); + + final OutputStream out = socket.getOutputStream(); + for (String argument : arguments) { + byte[] bytes = argument.getBytes(StandardCharsets.UTF_8); + if (bytes.length >= 0xFFFF) { + throw new IllegalArgumentException("Argument is too large"); + } + out.write(bytes.length >> 8); + out.write(bytes.length); + out.write(bytes); + retryScheduler.checkInterruptAndDelay(false /* sleepLonger */); + } + out.write(0xFF); + out.write(0xFF); + + // Wait for End-of-File. + final InputStream in = socket.getInputStream(); + while (true) { + try { + if (in.read() == -1) { + break; + } + } catch (Exception e) { + // ignore + } + retryScheduler.checkInterruptAndDelay(true /* sleepLonger */); + } + } + + @NonNull + public InetAddress resolve(final String endpoint) + throws ExecutionException, InterruptedException { + try { + return InetAddress.parseNumericAddress(endpoint); + } catch (IllegalArgumentException e) { + // Endpoint is not numeric : fall through and resolve + } + + final CancellationSignal cancellationSignal = new CancellationSignal(); + try { + final DnsResolver resolver = DnsResolver.getInstance(); + final CompletableFuture<InetAddress> result = new CompletableFuture(); + final DnsResolver.Callback<List<InetAddress>> cb = + new DnsResolver.Callback<List<InetAddress>>() { + @Override + public void onAnswer(@NonNull final List<InetAddress> answer, + final int rcode) { + if (answer.size() > 0) { + result.complete(answer.get(0)); + } else { + result.completeExceptionally( + new UnknownHostException(endpoint)); + } + } + + @Override + public void onError(@Nullable final DnsResolver.DnsException error) { + // Unfortunately UnknownHostException doesn't accept a cause, so + // print a message here instead. Only show the summary, not the + // full stack trace. + Log.e(TAG, "Async dns resolver error : " + error); + result.completeExceptionally(new UnknownHostException(endpoint)); + } + }; + resolver.query(null /* network, null for default */, endpoint, + DnsResolver.FLAG_EMPTY, r -> r.run(), cancellationSignal, cb); + return result.get(); + } catch (final ExecutionException e) { + Log.e(TAG, "Cannot resolve VPN endpoint : " + endpoint + ".", e); + throw e; + } catch (final InterruptedException e) { + Log.e(TAG, "Legacy VPN was interrupted while resolving the endpoint", e); + cancellationSignal.cancel(); + throw e; + } + } + + public boolean checkInterfacePresent(final Vpn vpn, final String iface) { + return vpn.jniCheck(iface) == 0; + } + } + public Vpn(Looper looper, Context context, INetworkManagementService netService, @UserIdInt int userHandle, @NonNull KeyStore keyStore) { - this(looper, context, netService, userHandle, keyStore, + this(looper, context, new Dependencies(), netService, userHandle, keyStore, new SystemServices(context), new Ikev2SessionCreator()); } @VisibleForTesting - protected Vpn(Looper looper, Context context, INetworkManagementService netService, + protected Vpn(Looper looper, Context context, Dependencies deps, + INetworkManagementService netService, int userHandle, @NonNull KeyStore keyStore, SystemServices systemServices, Ikev2SessionCreator ikev2SessionCreator) { mContext = context; + mDeps = deps; mNetd = netService; mUserHandle = userHandle; mLooper = looper; @@ -2129,7 +2261,8 @@ public class Vpn { } /** This class represents the common interface for all VPN runners. */ - private abstract class VpnRunner extends Thread { + @VisibleForTesting + abstract class VpnRunner extends Thread { protected VpnRunner(String name) { super(name); @@ -2638,7 +2771,7 @@ public class Vpn { } catch (InterruptedException e) { } for (String daemon : mDaemons) { - SystemService.stop(daemon); + mDeps.stopService(daemon); } } agentDisconnect(); @@ -2655,21 +2788,55 @@ public class Vpn { } } + private void checkAndFixupArguments(@NonNull final InetAddress endpointAddress) { + final String endpointAddressString = endpointAddress.getHostAddress(); + // Perform some safety checks before inserting the address in place. + // Position 0 in mDaemons and mArguments must be racoon, and position 1 must be mtpd. + if (!"racoon".equals(mDaemons[0]) || !"mtpd".equals(mDaemons[1])) { + throw new IllegalStateException("Unexpected daemons order"); + } + + // Respectively, the positions at which racoon and mtpd take the server address + // argument are 1 and 2. Not all types of VPN require both daemons however, and + // in that case the corresponding argument array is null. + if (mArguments[0] != null) { + if (!mProfile.server.equals(mArguments[0][1])) { + throw new IllegalStateException("Invalid server argument for racoon"); + } + mArguments[0][1] = endpointAddressString; + } + + if (mArguments[1] != null) { + if (!mProfile.server.equals(mArguments[1][2])) { + throw new IllegalStateException("Invalid server argument for mtpd"); + } + mArguments[1][2] = endpointAddressString; + } + } + private void bringup() { // Catch all exceptions so we can clean up a few things. try { + // resolve never returns null. If it does because of some bug, it will be + // caught by the catch() block below and cleanup gracefully. + final InetAddress endpointAddress = mDeps.resolve(mProfile.server); + + // Big hack : dynamically replace the address of the server in the arguments + // with the resolved address. + checkAndFixupArguments(endpointAddress); + // Initialize the timer. mBringupStartTime = SystemClock.elapsedRealtime(); // Wait for the daemons to stop. for (String daemon : mDaemons) { - while (!SystemService.isStopped(daemon)) { + while (!mDeps.isServiceStopped(daemon)) { checkInterruptAndDelay(true); } } // Clear the previous state. - File state = new File("/data/misc/vpn/state"); + final File state = mDeps.getStateFile(); state.delete(); if (state.exists()) { throw new IllegalStateException("Cannot delete the state"); @@ -2696,57 +2863,19 @@ public class Vpn { // Start the daemon. String daemon = mDaemons[i]; - SystemService.start(daemon); + mDeps.startService(daemon); // Wait for the daemon to start. - while (!SystemService.isRunning(daemon)) { + while (!mDeps.isServiceRunning(daemon)) { checkInterruptAndDelay(true); } // Create the control socket. mSockets[i] = new LocalSocket(); - LocalSocketAddress address = new LocalSocketAddress( - daemon, LocalSocketAddress.Namespace.RESERVED); - - // Wait for the socket to connect. - while (true) { - try { - mSockets[i].connect(address); - break; - } catch (Exception e) { - // ignore - } - checkInterruptAndDelay(true); - } - mSockets[i].setSoTimeout(500); - - // Send over the arguments. - OutputStream out = mSockets[i].getOutputStream(); - for (String argument : arguments) { - byte[] bytes = argument.getBytes(StandardCharsets.UTF_8); - if (bytes.length >= 0xFFFF) { - throw new IllegalArgumentException("Argument is too large"); - } - out.write(bytes.length >> 8); - out.write(bytes.length); - out.write(bytes); - checkInterruptAndDelay(false); - } - out.write(0xFF); - out.write(0xFF); - - // Wait for End-of-File. - InputStream in = mSockets[i].getInputStream(); - while (true) { - try { - if (in.read() == -1) { - break; - } - } catch (Exception e) { - // ignore - } - checkInterruptAndDelay(true); - } + + // Wait for the socket to connect and send over the arguments. + mDeps.sendArgumentsToDaemon(daemon, mSockets[i], arguments, + this::checkInterruptAndDelay); } // Wait for the daemons to create the new state. @@ -2754,7 +2883,7 @@ public class Vpn { // Check if a running daemon is dead. for (int i = 0; i < mDaemons.length; ++i) { String daemon = mDaemons[i]; - if (mArguments[i] != null && !SystemService.isRunning(daemon)) { + if (mArguments[i] != null && !mDeps.isServiceRunning(daemon)) { throw new IllegalStateException(daemon + " is dead"); } } @@ -2764,7 +2893,8 @@ public class Vpn { // Now we are connected. Read and parse the new state. String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1); if (parameters.length != 7) { - throw new IllegalStateException("Cannot parse the state"); + throw new IllegalStateException("Cannot parse the state: '" + + String.join("', '", parameters) + "'"); } // Set the interface and the addresses in the config. @@ -2793,20 +2923,15 @@ public class Vpn { } // Add a throw route for the VPN server endpoint, if one was specified. - String endpoint = parameters[5].isEmpty() ? mProfile.server : parameters[5]; - if (!endpoint.isEmpty()) { - try { - InetAddress addr = InetAddress.parseNumericAddress(endpoint); - if (addr instanceof Inet4Address) { - mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW)); - } else if (addr instanceof Inet6Address) { - mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW)); - } else { - Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint); - } - } catch (IllegalArgumentException e) { - Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e); - } + if (endpointAddress instanceof Inet4Address) { + mConfig.routes.add(new RouteInfo( + new IpPrefix(endpointAddress, 32), RTN_THROW)); + } else if (endpointAddress instanceof Inet6Address) { + mConfig.routes.add(new RouteInfo( + new IpPrefix(endpointAddress, 128), RTN_THROW)); + } else { + Log.e(TAG, "Unknown IP address family for VPN endpoint: " + + endpointAddress); } // Here is the last step and it must be done synchronously. @@ -2818,7 +2943,7 @@ public class Vpn { checkInterruptAndDelay(false); // Check if the interface is gone while we are waiting. - if (jniCheck(mConfig.interfaze) == 0) { + if (mDeps.checkInterfacePresent(Vpn.this, mConfig.interfaze)) { throw new IllegalStateException(mConfig.interfaze + " is gone"); } @@ -2849,7 +2974,7 @@ public class Vpn { while (true) { Thread.sleep(2000); for (int i = 0; i < mDaemons.length; i++) { - if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) { + if (mArguments[i] != null && mDeps.isServiceStopped(mDaemons[i])) { return; } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 74ed815f080a..5a4237938086 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -17,6 +17,7 @@ package com.android.server.input; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -66,6 +67,7 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -181,8 +183,7 @@ public class InputManagerService extends IInputManager.Stub // State for vibrator tokens. private Object mVibratorLock = new Object(); - private HashMap<IBinder, VibratorToken> mVibratorTokens = - new HashMap<IBinder, VibratorToken>(); + private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>(); private int mNextVibratorTokenValue; // State for the currently installed input filter. @@ -190,12 +191,16 @@ public class InputManagerService extends IInputManager.Stub IInputFilter mInputFilter; // guarded by mInputFilterLock InputFilterHost mInputFilterHost; // guarded by mInputFilterLock + private final Object mGestureMonitorPidsLock = new Object(); + @GuardedBy("mGestureMonitorPidsLock") + private final ArrayMap<IBinder, Integer> mGestureMonitorPidsByToken = new ArrayMap<>(); + // The associations of input devices to displays by port. Maps from input device port (String) // to display id (int). Currently only accessed by InputReader. private final Map<String, Integer> mStaticAssociations; private final Object mAssociationsLock = new Object(); @GuardedBy("mAssociationLock") - private final Map<String, Integer> mRuntimeAssociations = new HashMap<String, Integer>(); + private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<String, Integer>(); private static native long nativeInit(InputManagerService service, Context context, MessageQueue messageQueue); @@ -540,13 +545,17 @@ public class InputManagerService extends IInputManager.Stub if (displayId < Display.DEFAULT_DISPLAY) { throw new IllegalArgumentException("displayId must >= 0."); } + final int pid = Binder.getCallingPid(); final long ident = Binder.clearCallingIdentity(); try { InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName); InputMonitorHost host = new InputMonitorHost(inputChannels[0]); - nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId, - true /*isGestureMonitor*/); + nativeRegisterInputMonitor( + mPtr, inputChannels[0], displayId, true /*isGestureMonitor*/); + synchronized (mGestureMonitorPidsLock) { + mGestureMonitorPidsByToken.put(inputChannels[1].getToken(), pid); + } return new InputMonitor(inputChannels[1], host); } finally { Binder.restoreCallingIdentity(ident); @@ -575,6 +584,9 @@ public class InputManagerService extends IInputManager.Stub if (inputChannel == null) { throw new IllegalArgumentException("inputChannel must not be null."); } + synchronized (mGestureMonitorPidsLock) { + mGestureMonitorPidsByToken.remove(inputChannel.getToken()); + } nativeUnregisterInputChannel(mPtr, inputChannel); } @@ -1838,6 +1850,7 @@ public class InputManagerService extends IInputManager.Stub if (dumpStr != null) { pw.println(dumpStr); dumpAssociations(pw); + dumpGestureMonitorPidsByToken(pw); } } @@ -1861,6 +1874,19 @@ public class InputManagerService extends IInputManager.Stub } } + private void dumpGestureMonitorPidsByToken(PrintWriter pw) { + synchronized (mGestureMonitorPidsLock) { + if (!mGestureMonitorPidsByToken.isEmpty()) { + pw.println("Gesture monitor pids by token:"); + for (int i = 0; i < mGestureMonitorPidsByToken.size(); i++) { + pw.print(" " + i + ": "); + pw.print(" token: " + mGestureMonitorPidsByToken.keyAt(i)); + pw.println(" pid: " + mGestureMonitorPidsByToken.valueAt(i)); + } + } + } + } + private boolean checkCallingPermission(String permission, String func) { // Quick check: if the calling permission is me, it's all okay. if (Binder.getCallingPid() == Process.myPid()) { @@ -1883,6 +1909,7 @@ public class InputManagerService extends IInputManager.Stub public void monitor() { synchronized (mInputFilterLock) { } synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */} + synchronized (mGestureMonitorPidsLock) { /* Test if blocked by gesture monitor pids lock */} nativeMonitor(mPtr); } @@ -1944,6 +1971,9 @@ public class InputManagerService extends IInputManager.Stub // Native callback. private void notifyInputChannelBroken(IBinder token) { + synchronized (mGestureMonitorPidsLock) { + mGestureMonitorPidsByToken.remove(token); + } mWindowManagerCallbacks.notifyInputChannelBroken(token); } @@ -1959,8 +1989,12 @@ public class InputManagerService extends IInputManager.Stub // Native callback. private long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, String reason) { - return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, - token, reason); + Integer gestureMonitorPid; + synchronized (mGestureMonitorPidsLock) { + gestureMonitorPid = mGestureMonitorPidsByToken.get(token); + } + return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, token, gestureMonitorPid, + reason); } // Native callback. @@ -2206,22 +2240,48 @@ public class InputManagerService extends IInputManager.Stub * Callback interface implemented by the Window Manager. */ public interface WindowManagerCallbacks { + /** + * This callback is invoked when the confuguration changes. + */ public void notifyConfigurationChanged(); + /** + * This callback is invoked when the lid switch changes state. + * @param whenNanos the time when the change occurred + * @param lidOpen true if the lid is open + */ public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen); + /** + * This callback is invoked when the camera lens cover switch changes state. + * @param whenNanos the time when the change occurred + * @param lensCovered true is the lens is covered + */ public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered); + /** + * This callback is invoked when an input channel is closed unexpectedly. + * @param token the connection token of the broken channel + */ public void notifyInputChannelBroken(IBinder token); /** - * Notifies the window manager about an application that is not responding. - * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. + * Notify the window manager about an application that is not responding. + * Return a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. */ long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, - String reason); + @Nullable Integer pid, String reason); - public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags); + /** + * This callback is invoked when an event first arrives to InputDispatcher and before it is + * placed onto InputDispatcher's queue. If this event is intercepted, it will never be + * processed by InputDispacher. + * @param event The key event that's arriving to InputDispatcher + * @param policyFlags The policy flags + * @return the flags that tell InputDispatcher how to handle the event (for example, whether + * to pass it to the user) + */ + int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags); /** * Provides an opportunity for the window manager policy to intercept early motion event @@ -2231,11 +2291,23 @@ public class InputManagerService extends IInputManager.Stub int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, int policyFlags); - public long interceptKeyBeforeDispatching(IBinder token, - KeyEvent event, int policyFlags); + /** + * This callback is invoked just before the key is about to be sent to an application. + * This allows the policy to make some last minute decisions on whether to intercept this + * key. + * @param token the window token that's about to receive this event + * @param event the key event that's being dispatched + * @param policyFlags the policy flags + * @return negative value if the key should be skipped (not sent to the app). 0 if the key + * should proceed getting dispatched to the app. positive value to indicate the additional + * time delay, in nanoseconds, to wait before sending this key to the app. + */ + long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags); - public KeyEvent dispatchUnhandledKey(IBinder token, - KeyEvent event, int policyFlags); + /** + * Dispatch unhandled key + */ + KeyEvent dispatchUnhandledKey(IBinder token, KeyEvent event, int policyFlags); public int getPointerLayer(); diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java index 1f458ed4e29d..6f35c8ba1e0a 100644 --- a/services/core/java/com/android/server/location/LocationFudger.java +++ b/services/core/java/com/android/server/location/LocationFudger.java @@ -112,7 +112,7 @@ public class LocationFudger { public Location createCoarse(Location fine) { synchronized (this) { if (fine == mCachedFineLocation) { - return new Location(mCachedCoarseLocation); + return mCachedCoarseLocation; } } @@ -154,7 +154,7 @@ public class LocationFudger { mCachedCoarseLocation = coarse; } - return new Location(mCachedCoarseLocation); + return mCachedCoarseLocation; } /** diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index d3558f1458b8..71f1833854ce 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -17,43 +17,27 @@ package com.android.server.location; import static android.Manifest.permission.ACCESS_FINE_LOCATION; -import static android.app.AppOpsManager.OP_MOCK_LOCATION; -import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; -import static android.app.AppOpsManager.OP_MONITOR_LOCATION; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.location.LocationManager.EXTRA_LOCATION_ENABLED; -import static android.location.LocationManager.EXTRA_PROVIDER_ENABLED; -import static android.location.LocationManager.EXTRA_PROVIDER_NAME; import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER; -import static android.location.LocationManager.KEY_LOCATION_CHANGED; -import static android.location.LocationManager.KEY_PROVIDER_ENABLED; -import static android.location.LocationManager.MODE_CHANGED_ACTION; import static android.location.LocationManager.NETWORK_PROVIDER; -import static android.location.LocationManager.PASSIVE_PROVIDER; -import static android.location.LocationManager.PROVIDERS_CHANGED_ACTION; -import static android.location.LocationManager.invalidateLocalLocationEnabledCaches; -import static android.os.PowerManager.locationPowerSaveModeToString; import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; import static com.android.server.location.LocationPermissions.PERMISSION_FINE; -import static com.android.server.location.LocationPermissions.PERMISSION_NONE; +import static com.android.server.location.LocationProviderManager.FASTEST_COARSE_INTERVAL_MS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import android.Manifest.permission; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.location.Criteria; import android.location.GeocoderParams; import android.location.Geofence; @@ -78,17 +62,9 @@ import android.location.LocationRequest; import android.location.LocationTime; import android.location.util.identity.CallerIdentity; import android.os.Binder; -import android.os.Build; import android.os.Bundle; -import android.os.CancellationSignal; -import android.os.Handler; -import android.os.IBinder; import android.os.ICancellationSignal; -import android.os.IRemoteCallback; import android.os.ParcelFileDescriptor; -import android.os.PowerManager; -import android.os.PowerManager.ServiceType; -import android.os.PowerManagerInternal; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; @@ -96,24 +72,16 @@ import android.os.UserHandle; import android.os.WorkSource; import android.os.WorkSource.WorkChain; import android.stats.location.LocationStatsEnums; -import android.text.TextUtils; -import android.util.EventLog; import android.util.IndentingPrintWriter; import android.util.Log; -import android.util.SparseArray; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; -import com.android.internal.content.PackageMonitor; import com.android.internal.location.ProviderProperties; -import com.android.internal.location.ProviderRequest; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; -import com.android.server.FgThread; import com.android.server.LocalServices; -import com.android.server.PendingIntentUtils; import com.android.server.SystemService; -import com.android.server.location.AbstractLocationProvider.State; import com.android.server.location.LocationPermissions.PermissionLevel; import com.android.server.location.LocationRequestStatistics.PackageProviderKey; import com.android.server.location.LocationRequestStatistics.PackageStatistics; @@ -137,24 +105,17 @@ import com.android.server.location.util.SystemScreenInteractiveHelper; import com.android.server.location.util.SystemSettingsHelper; import com.android.server.location.util.SystemUserInfoHelper; import com.android.server.location.util.UserInfoHelper; -import com.android.server.location.util.UserInfoHelper.UserListener; import com.android.server.pm.permission.PermissionManagerServiceInternal; -import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; -import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Objects; import java.util.TreeMap; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Executor; /** * The service class that manages LocationProviders and issues location @@ -241,54 +202,21 @@ public class LocationManagerService extends ILocationManager.Stub { public static final String TAG = "LocationManagerService"; public static final boolean D = Log.isLoggable(TAG, Log.DEBUG); - private static final String WAKELOCK_KEY = "*location*"; - private static final String NETWORK_LOCATION_SERVICE_ACTION = "com.android.location.service.v3.NetworkLocationProvider"; private static final String FUSED_LOCATION_SERVICE_ACTION = "com.android.location.service.FusedLocationProvider"; - // The maximum interval a location request can have and still be considered "high power". - private static final long HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000; - - // The fastest interval that applications can receive coarse locations - private static final long FASTEST_COARSE_INTERVAL_MS = 10 * 60 * 1000; - - // maximum age of a location before it is no longer considered "current" - private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000; - - // Location Providers may sometimes deliver location updates - // slightly faster that requested - provide grace period so - // we don't unnecessarily filter events that are otherwise on - // time - private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100; - - private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30000; - private static final String ATTRIBUTION_TAG = "LocationService"; private final Object mLock = new Object(); - private final Handler mHandler; - private final LocalService mLocalService; - - private final Injector mInjector; - private final Context mContext; - private final AppOpsHelper mAppOpsHelper; - private final UserInfoHelper mUserInfoHelper; - private final SettingsHelper mSettingsHelper; - private final AppForegroundHelper mAppForegroundHelper; - private final LocationUsageLogger mLocationUsageLogger; + private final Injector mInjector; + private final LocalService mLocalService; private final GeofenceManager mGeofenceManager; - @Nullable private volatile GnssManagerService mGnssManagerService = null; - - private final PassiveLocationProviderManager mPassiveManager; - - private PowerManager mPowerManager; - private GeocoderProxy mGeocodeProvider; @GuardedBy("mLock") @@ -296,46 +224,30 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") private boolean mExtraLocationControllerPackageEnabled; - // @GuardedBy("mLock") - // hold lock for write or to prevent write, no lock for read - final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers = - new CopyOnWriteArrayList<>(); - - @GuardedBy("mLock") - private final HashMap<Object, Receiver> mReceivers = new HashMap<>(); - private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider = - new HashMap<>(); + // location provider managers - private final LocationRequestStatistics mRequestStatistics = new LocationRequestStatistics(); + private final PassiveLocationProviderManager mPassiveManager; - @GuardedBy("mLock") - @PowerManager.LocationPowerSaveMode - private int mBatterySaverMode; + // @GuardedBy("mProviderManagers") + // hold lock for writes, no lock necessary for simple reads + private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers = + new CopyOnWriteArrayList<>(); LocationManagerService(Context context, Injector injector) { - mHandler = FgThread.getHandler(); - mLocalService = new LocalService(); - - LocalServices.addService(LocationManagerInternal.class, mLocalService); - + mContext = context.createAttributionContext(ATTRIBUTION_TAG); mInjector = injector; - mContext = context.createAttributionContext(ATTRIBUTION_TAG); - mUserInfoHelper = injector.getUserInfoHelper(); - mAppOpsHelper = injector.getAppOpsHelper(); - mSettingsHelper = injector.getSettingsHelper(); - mAppForegroundHelper = injector.getAppForegroundHelper(); - mLocationUsageLogger = injector.getLocationUsageLogger(); + mLocalService = new LocalService(); + LocalServices.addService(LocationManagerInternal.class, mLocalService); mGeofenceManager = new GeofenceManager(mContext, injector); - // set up passive provider - we do this early because it has no dependencies on system - // services or external code that isn't ready yet, and because this allows the variable to - // be final. other more complex providers are initialized later, when system services are - // ready - mPassiveManager = new PassiveLocationProviderManager(); - mProviderManagers.add(mPassiveManager); - mPassiveManager.setRealProvider(new PassiveProvider(mContext)); + // set up passive provider first since it will be required for all other location providers, + // which are loaded later once the system is ready. + mPassiveManager = new PassiveLocationProviderManager(mContext, injector); + addLocationProviderManager(mPassiveManager, new PassiveProvider(mContext)); + + // TODO: load the gps provider here as well, which will require refactoring // Let the package manager query which are the default location // providers as they get certain permissions granted by default. @@ -347,253 +259,77 @@ public class LocationManagerService extends ILocationManager.Stub { permissionManagerInternal.setLocationExtraPackagesProvider( userId -> mContext.getResources().getStringArray( com.android.internal.R.array.config_locationExtraPackageNames)); - - // most startup is deferred until systemReady() - } - - void onSystemReady() { - synchronized (mLock) { - mPowerManager = mContext.getSystemService(PowerManager.class); - - // add listeners - mContext.getPackageManager().addOnPermissionsChangeListener( - uid -> { - // listener invoked on ui thread, move to our thread to reduce risk of - // blocking ui thread - mHandler.post(() -> { - synchronized (mLock) { - onPermissionsChangedLocked(); - } - }); - }); - - LocalServices.getService(PowerManagerInternal.class).registerLowPowerModeObserver( - ServiceType.LOCATION, - state -> { - // listener invoked on ui thread, move to our thread to reduce risk of - // blocking ui thread - mHandler.post(() -> { - synchronized (mLock) { - onBatterySaverModeChangedLocked(state.locationMode); - } - }); - }); - mBatterySaverMode = mPowerManager.getLocationPowerSaveMode(); - - mAppOpsHelper.addListener(this::onAppOpChanged); - - mSettingsHelper.addOnLocationEnabledChangedListener(this::onLocationModeChanged); - mSettingsHelper.addOnBackgroundThrottleIntervalChangedListener( - this::onBackgroundThrottleIntervalChanged); - mSettingsHelper.addOnBackgroundThrottlePackageWhitelistChangedListener( - this::onBackgroundThrottleWhitelistChanged); - mSettingsHelper.addOnIgnoreSettingsPackageWhitelistChangedListener( - this::onIgnoreSettingsWhitelistChanged); - - PackageMonitor packageMonitor = new PackageMonitor() { - @Override - public void onPackageDisappeared(String packageName, int reason) { - synchronized (mLock) { - LocationManagerService.this.onPackageDisappeared(packageName); - } - } - }; - packageMonitor.register(mContext, null, true, mHandler); - - mUserInfoHelper.addListener(this::onUserChanged); - - mAppForegroundHelper.addListener(this::onAppForegroundChanged); - - IntentFilter screenIntentFilter = new IntentFilter(); - screenIntentFilter.addAction(Intent.ACTION_SCREEN_OFF); - screenIntentFilter.addAction(Intent.ACTION_SCREEN_ON); - mContext.registerReceiverAsUser(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_SCREEN_ON.equals(intent.getAction()) - || Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { - onScreenStateChanged(); - } - } - }, UserHandle.ALL, screenIntentFilter, null, mHandler); - - // initialize the current users. we would get the user started notifications for these - // users eventually anyways, but this takes care of it as early as possible. - onUserChanged(UserHandle.USER_ALL, UserListener.USER_STARTED); - } } - void onSystemThirdPartyAppsCanStart() { - synchronized (mLock) { - // prepare providers - initializeProvidersLocked(); - } - - // initialize gnss last because it has no awareness of boot phases and blindly assumes that - // all other location providers are loaded at initialization - initializeGnss(); - } - - private void onAppOpChanged(String packageName) { - synchronized (mLock) { - for (Receiver receiver : mReceivers.values()) { - if (receiver.mCallerIdentity.getPackageName().equals(packageName)) { - receiver.updateMonitoring(true); - } - } - - HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size()); - for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) { - String provider = entry.getKey(); - for (UpdateRecord record : entry.getValue()) { - if (record.mReceiver.mCallerIdentity.getPackageName().equals(packageName)) { - affectedProviders.add(provider); - } - } - } - for (String provider : affectedProviders) { - applyRequirementsLocked(provider); - } - } - } - - @GuardedBy("mLock") - private void onPermissionsChangedLocked() { - for (LocationProviderManager manager : mProviderManagers) { - applyRequirementsLocked(manager); - } - } - - @GuardedBy("mLock") - private void onBatterySaverModeChangedLocked(int newLocationMode) { - if (mBatterySaverMode == newLocationMode) { - return; - } - - if (D) { - Log.d(TAG, - "Battery Saver location mode changed from " - + locationPowerSaveModeToString(mBatterySaverMode) + " to " - + locationPowerSaveModeToString(newLocationMode)); + @Nullable + private LocationProviderManager getLocationProviderManager(String providerName) { + if (providerName == null) { + return null; } - mBatterySaverMode = newLocationMode; - for (LocationProviderManager manager : mProviderManagers) { - applyRequirementsLocked(manager); - } - } - - private void onScreenStateChanged() { - synchronized (mLock) { - if (mBatterySaverMode == PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF) { - for (LocationProviderManager manager : mProviderManagers) { - applyRequirementsLocked(manager); - } + if (providerName.equals(manager.getName())) { + return manager; } } - } - - private void onLocationModeChanged(int userId) { - boolean enabled = mSettingsHelper.isLocationEnabled(userId); - LocationManager.invalidateLocalLocationEnabledCaches(); - if (D) { - Log.d(TAG, "[u" + userId + "] location enabled = " + enabled); - } - - Intent intent = new Intent(MODE_CHANGED_ACTION) - .putExtra(EXTRA_LOCATION_ENABLED, enabled) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); - - synchronized (mLock) { - for (LocationProviderManager manager : mProviderManagers) { - manager.onEnabledChangedLocked(userId); - } - } + return null; } - private void onPackageDisappeared(String packageName) { - synchronized (mLock) { - ArrayList<Receiver> deadReceivers = null; - - for (Receiver receiver : mReceivers.values()) { - if (receiver.mCallerIdentity.getPackageName().equals(packageName)) { - if (deadReceivers == null) { - deadReceivers = new ArrayList<>(); - } - deadReceivers.add(receiver); + private LocationProviderManager getOrAddLocationProviderManager(String providerName) { + synchronized (mProviderManagers) { + for (LocationProviderManager manager : mProviderManagers) { + if (providerName.equals(manager.getName())) { + return manager; } } - // perform removal outside of mReceivers loop - if (deadReceivers != null) { - for (Receiver receiver : deadReceivers) { - removeUpdatesLocked(receiver); - } - } + LocationProviderManager manager = new LocationProviderManager(mContext, mInjector, + providerName, mPassiveManager); + addLocationProviderManager(manager, null); + return manager; } } - private void onAppForegroundChanged(int uid, boolean foreground) { - synchronized (mLock) { - HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size()); - for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) { - String provider = entry.getKey(); - for (UpdateRecord record : entry.getValue()) { - if (record.mReceiver.mCallerIdentity.getUid() == uid - && record.mIsForegroundUid != foreground) { - record.updateForeground(foreground); - - if (!isThrottlingExempt(record.mReceiver.mCallerIdentity)) { - affectedProviders.add(provider); - } - } - } - } - for (String provider : affectedProviders) { - applyRequirementsLocked(provider); - } - } - } + private void addLocationProviderManager(LocationProviderManager manager, + @Nullable AbstractLocationProvider realProvider) { + synchronized (mProviderManagers) { + Preconditions.checkState(getLocationProviderManager(manager.getName()) == null); - private void onBackgroundThrottleIntervalChanged() { - synchronized (mLock) { - for (LocationProviderManager manager : mProviderManagers) { - applyRequirementsLocked(manager); + manager.startManager(); + if (realProvider != null) { + manager.setRealProvider(realProvider); } + mProviderManagers.add(manager); } } - private void onBackgroundThrottleWhitelistChanged() { - synchronized (mLock) { - for (LocationProviderManager manager : mProviderManagers) { - applyRequirementsLocked(manager); - } + private void removeLocationProviderManager(LocationProviderManager manager) { + synchronized (mProviderManagers) { + Preconditions.checkState(getLocationProviderManager(manager.getName()) == manager); + + mProviderManagers.remove(manager); + manager.setMockProvider(null); + manager.setRealProvider(null); + manager.stopManager(); } } - private void onIgnoreSettingsWhitelistChanged() { - synchronized (mLock) { - for (LocationProviderManager manager : mProviderManagers) { - applyRequirementsLocked(manager); - } - } + void onSystemReady() { + mInjector.getSettingsHelper().addOnLocationEnabledChangedListener( + this::onLocationModeChanged); } - @GuardedBy("mLock") - private void initializeProvidersLocked() { + void onSystemThirdPartyAppsCanStart() { LocationProviderProxy networkProvider = LocationProviderProxy.createAndRegister( mContext, NETWORK_LOCATION_SERVICE_ACTION, com.android.internal.R.bool.config_enableNetworkLocationOverlay, com.android.internal.R.string.config_networkLocationProviderPackageName); if (networkProvider != null) { - LocationProviderManager networkManager = new LocationProviderManager(NETWORK_PROVIDER); - mProviderManagers.add(networkManager); - networkManager.setRealProvider(networkProvider); + LocationProviderManager networkManager = new LocationProviderManager(mContext, + mInjector, NETWORK_PROVIDER, mPassiveManager); + addLocationProviderManager(networkManager, networkProvider); } else { Log.w(TAG, "no network location provider found"); } @@ -604,18 +340,28 @@ public class LocationManagerService extends ILocationManager.Stub { MATCH_DIRECT_BOOT_AWARE | MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM).isEmpty(), "Unable to find a direct boot aware fused location provider"); - // bind to fused provider LocationProviderProxy fusedProvider = LocationProviderProxy.createAndRegister( mContext, FUSED_LOCATION_SERVICE_ACTION, com.android.internal.R.bool.config_enableFusedLocationOverlay, com.android.internal.R.string.config_fusedLocationProviderPackageName); if (fusedProvider != null) { - LocationProviderManager fusedManager = new LocationProviderManager(FUSED_PROVIDER); - mProviderManagers.add(fusedManager); - fusedManager.setRealProvider(fusedProvider); + LocationProviderManager fusedManager = new LocationProviderManager(mContext, mInjector, + FUSED_PROVIDER, mPassiveManager); + addLocationProviderManager(fusedManager, fusedProvider); } else { - Log.e(TAG, "no fused location provider found"); + Log.wtf(TAG, "no fused location provider found"); + } + + // initialize gnss last because it has no awareness of boot phases and blindly assumes that + // all other location providers are loaded at initialization + if (GnssManagerService.isGnssSupported()) { + mGnssManagerService = new GnssManagerService(mContext, mInjector); + mGnssManagerService.onSystemReady(); + + LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector, + GPS_PROVIDER, mPassiveManager); + addLocationProviderManager(gnssManager, mGnssManagerService.getGnssLocationProvider()); } // bind to geocoder provider @@ -631,6 +377,18 @@ public class LocationManagerService extends ILocationManager.Stub { Log.e(TAG, "unable to bind ActivityRecognitionProxy"); } + // bind to gnss geofence proxy + if (GnssManagerService.isGnssSupported()) { + IGpsGeofenceHardware gpsGeofenceHardware = mGnssManagerService.getGpsGeofenceProxy(); + if (gpsGeofenceHardware != null) { + GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, gpsGeofenceHardware); + if (provider == null) { + Log.e(TAG, "unable to bind to GeofenceProxy"); + } + } + } + + // create any predefined test providers String[] testProviderStrings = mContext.getResources().getStringArray( com.android.internal.R.array.config_testLocationProviders); for (String testProviderString : testProviderStrings) { @@ -646,763 +404,24 @@ public class LocationManagerService extends ILocationManager.Stub { Boolean.parseBoolean(fragments[7]) /* supportsBearing */, Integer.parseInt(fragments[8]) /* powerRequirement */, Integer.parseInt(fragments[9]) /* accuracy */); - LocationProviderManager manager = getLocationProviderManager(name); - if (manager == null) { - manager = new LocationProviderManager(name); - mProviderManagers.add(manager); - } - manager.setMockProvider( + getOrAddLocationProviderManager(name).setMockProvider( new MockProvider(properties, CallerIdentity.fromContext(mContext))); } } - private void initializeGnss() { - // Do not hold mLock when calling GnssManagerService#isGnssSupported() which calls into HAL. - if (GnssManagerService.isGnssSupported()) { - mGnssManagerService = new GnssManagerService(mContext, mInjector); - mGnssManagerService.onSystemReady(); - - LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER); - synchronized (mLock) { - mProviderManagers.add(gnssManager); - } - gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider()); - - // bind to geofence proxy - IGpsGeofenceHardware gpsGeofenceHardware = mGnssManagerService.getGpsGeofenceProxy(); - if (gpsGeofenceHardware != null) { - GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, gpsGeofenceHardware); - if (provider == null) { - Log.e(TAG, "unable to bind to GeofenceProxy"); - } - } - } - } - - private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) { - switch (change) { - case UserListener.CURRENT_USER_CHANGED: - synchronized (mLock) { - for (LocationProviderManager manager : mProviderManagers) { - manager.onEnabledChangedLocked(userId); - } - } - break; - case UserListener.USER_STARTED: - synchronized (mLock) { - for (LocationProviderManager manager : mProviderManagers) { - manager.onUserStarted(userId); - } - } - break; - case UserListener.USER_STOPPED: - synchronized (mLock) { - for (LocationProviderManager manager : mProviderManagers) { - manager.onUserStopped(userId); - } - } - break; - } - } - - /** - * Location provider manager, manages a LocationProvider. - */ - class LocationProviderManager implements MockableLocationProvider.Listener { - - private final String mName; - - private final LocationFudger mLocationFudger; - - // if the provider is enabled for a given user id - null or not present means unknown - @GuardedBy("mLock") - private final SparseArray<Boolean> mEnabled; - - // last location for a given user - @GuardedBy("mLock") - private final SparseArray<Location> mLastLocation; - - // last coarse location for a given user - @GuardedBy("mLock") - private final SparseArray<Location> mLastCoarseLocation; - - // acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary - protected final MockableLocationProvider mProvider; - - LocationProviderManager(String name) { - mName = name; - mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM()); - mEnabled = new SparseArray<>(2); - mLastLocation = new SparseArray<>(2); - mLastCoarseLocation = new SparseArray<>(2); - - // initialize last since this lets our reference escape - mProvider = new MockableLocationProvider(mLock, this); - } - - public String getName() { - return mName; - } - - public boolean hasProvider() { - return mProvider.getProvider() != null; - } - - public void setRealProvider(AbstractLocationProvider provider) { - mProvider.setRealProvider(provider); - } - - public void setMockProvider(@Nullable MockProvider provider) { - synchronized (mLock) { - mProvider.setMockProvider(provider); - - // when removing a mock provider, also clear any mock last locations and reset the - // location fudger. the mock provider could have been used to infer the current - // location fudger offsets. - if (provider == null) { - for (int i = 0; i < mLastLocation.size(); i++) { - Location lastLocation = mLastLocation.valueAt(i); - if (lastLocation != null && lastLocation.isFromMockProvider()) { - mLastLocation.setValueAt(i, null); - } - } - - for (int i = 0; i < mLastCoarseLocation.size(); i++) { - Location lastCoarseLocation = mLastCoarseLocation.valueAt(i); - if (lastCoarseLocation != null && lastCoarseLocation.isFromMockProvider()) { - mLastCoarseLocation.setValueAt(i, null); - } - } - - mLocationFudger.resetOffsets(); - } - } - } - - @Nullable - public CallerIdentity getProviderIdentity() { - return mProvider.getState().identity; - } - - @Nullable - public ProviderProperties getProperties() { - return mProvider.getState().properties; - } - - @Nullable - public Location getLastLocation(int userId, @PermissionLevel int permissionlevel) { - synchronized (mLock) { - switch (permissionlevel) { - case PERMISSION_COARSE: - return mLastCoarseLocation.get(userId); - case PERMISSION_FINE: - return mLastLocation.get(userId); - default: - throw new AssertionError(); - } - } - } - - public void injectLastLocation(Location location, int userId) { - synchronized (mLock) { - if (mLastLocation.get(userId) == null) { - setLastLocation(location, userId); - } - } - } - - private void setLastLocation(Location location, int userId) { - synchronized (mLock) { - mLastLocation.put(userId, location); - - // update last coarse interval only if enough time has passed - long timeDeltaMs = Long.MAX_VALUE; - Location coarseLocation = mLastCoarseLocation.get(userId); - if (coarseLocation != null) { - timeDeltaMs = NANOSECONDS.toMillis(location.getElapsedRealtimeNanos()) - - NANOSECONDS.toMillis(coarseLocation.getElapsedRealtimeNanos()); - } - if (timeDeltaMs > FASTEST_COARSE_INTERVAL_MS) { - mLastCoarseLocation.put(userId, mLocationFudger.createCoarse(location)); - } - } - } - - public void setMockProviderAllowed(boolean enabled) { - synchronized (mLock) { - if (!mProvider.isMock()) { - throw new IllegalArgumentException(mName + " provider is not a test provider"); - } - - mProvider.setMockProviderAllowed(enabled); - } - } - - public void setMockProviderLocation(Location location) { - synchronized (mLock) { - if (!mProvider.isMock()) { - throw new IllegalArgumentException(mName + " provider is not a test provider"); - } - - String locationProvider = location.getProvider(); - if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) { - // The location has an explicit provider that is different from the mock - // provider name. The caller may be trying to fool us via b/33091107. - EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(), - mName + "!=" + locationProvider); - } - - mProvider.setMockProviderLocation(location); - } - } - - public List<LocationRequest> getMockProviderRequests() { - synchronized (mLock) { - if (!mProvider.isMock()) { - throw new IllegalArgumentException(mName + " provider is not a test provider"); - } - - return mProvider.getCurrentRequest().locationRequests; - } - } - - public void setRequest(ProviderRequest request) { - mProvider.setRequest(request); - } - - public void sendExtraCommand(int uid, int pid, String command, Bundle extras) { - mProvider.sendExtraCommand(uid, pid, command, extras); - } - - @GuardedBy("mLock") - @Override - public void onReportLocation(Location location) { - // don't validate mock locations - if (!location.isFromMockProvider()) { - if (location.getLatitude() == 0 && location.getLongitude() == 0) { - Log.w(TAG, "blocking 0,0 location from " + mName + " provider"); - return; - } - } - - if (!location.isComplete()) { - Log.w(TAG, "blocking incomplete location from " + mName + " provider"); - return; - } - - // update last location if the provider is enabled or if servicing a bypass request - boolean locationSettingsIgnored = mProvider.getCurrentRequest().locationSettingsIgnored; - for (int userId : mUserInfoHelper.getRunningUserIds()) { - if (locationSettingsIgnored || isEnabled(userId)) { - setLastLocation(location, userId); - } - } - - handleLocationChangedLocked(this, location, mLocationFudger.createCoarse(location)); - } - - @GuardedBy("mLock") - @Override - public void onReportLocation(List<Location> locations) { - if (mGnssManagerService == null || !GPS_PROVIDER.equals(mName)) { - return; - } - - mGnssManagerService.onReportLocation(locations); - } - - @GuardedBy("mLock") - @Override - public void onStateChanged(State oldState, State newState) { - if (oldState.allowed != newState.allowed) { - if (D) { - Log.d(TAG, mName + " provider allowed = " + newState.allowed); - } - - onEnabledChangedLocked(UserHandle.USER_ALL); - } - } - - public void onUserStarted(int userId) { - if (userId == UserHandle.USER_NULL) { - return; - } else if (userId == UserHandle.USER_ALL) { - for (int runningUserId : mUserInfoHelper.getRunningUserIds()) { - onUserStarted(runningUserId); - } - return; - } - - Preconditions.checkArgument(userId >= 0); - - synchronized (mLock) { - // clear the user's prior enabled state to prevent broadcast of enabled state - // change. user starts should never result in a broadcast since the state has - // technically not changed. - mEnabled.put(userId, null); - onEnabledChangedLocked(userId); - } - } - - public void onUserStopped(int userId) { - if (userId == UserHandle.USER_NULL) { - return; - } else if (userId == UserHandle.USER_ALL) { - mEnabled.clear(); - mLastLocation.clear(); - mLastCoarseLocation.clear(); - return; - } - - Preconditions.checkArgument(userId >= 0); - - synchronized (mLock) { - mEnabled.remove(userId); - mLastLocation.remove(userId); - mLastCoarseLocation.remove(userId); - } - } - - public boolean isEnabled(int userId) { - if (userId == UserHandle.USER_NULL) { - // used during initialization - ignore since many lower level operations (checking - // settings for instance) do not support the null user - return false; - } - - Preconditions.checkArgument(userId >= 0); - - synchronized (mLock) { - Boolean enabled = mEnabled.get(userId); - if (enabled == null) { - // this generally shouldn't occur, but might be possible due to race conditions - // on when we are notified of new users - Log.w(TAG, mName + " provider saw user " + userId + " unexpectedly"); - onEnabledChangedLocked(userId); - enabled = Objects.requireNonNull(mEnabled.get(userId)); - } - - return enabled; - } - } - - @GuardedBy("mLock") - public void onEnabledChangedLocked(int userId) { - if (userId == UserHandle.USER_NULL) { - // used during initialization - ignore since many lower level operations (checking - // settings for instance) do not support the null user - return; - } else if (userId == UserHandle.USER_ALL) { - for (int runningUserId : mUserInfoHelper.getRunningUserIds()) { - onEnabledChangedLocked(runningUserId); - } - return; - } - - Preconditions.checkArgument(userId >= 0); - - // if any property that contributes to "enabled" here changes state, it MUST result - // in a direct or indrect call to onEnabledChangedLocked. this allows the provider to - // guarantee that it will always eventually reach the correct state. - boolean enabled = mProvider.getState().allowed - && mUserInfoHelper.isCurrentUserId(userId) - && mSettingsHelper.isLocationEnabled(userId); - - Boolean wasEnabled = mEnabled.get(userId); - if (wasEnabled != null && wasEnabled == enabled) { - return; - } - - mEnabled.put(userId, enabled); - - if (D) { - Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled); - } - - // clear last locations if we become disabled and if not servicing a bypass request - if (!enabled && !mProvider.getCurrentRequest().locationSettingsIgnored) { - mLastLocation.put(userId, null); - mLastCoarseLocation.put(userId, null); - } - - // do not send change notifications if we just saw this user for the first time - if (wasEnabled != null) { - // fused and passive provider never get public updates for legacy reasons - if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) { - Intent intent = new Intent(PROVIDERS_CHANGED_ACTION) - .putExtra(EXTRA_PROVIDER_NAME, mName) - .putExtra(EXTRA_PROVIDER_ENABLED, enabled) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); - } - } - - updateProviderEnabledLocked(this, enabled); - } - - public void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { - synchronized (mLock) { - pw.print(mName + " provider"); - if (mProvider.isMock()) { - pw.print(" [mock]"); - } - pw.println(":"); - - pw.increaseIndent(); - - int[] userIds = mUserInfoHelper.getRunningUserIds(); - for (int userId : userIds) { - if (userIds.length != 1) { - pw.println("user " + userId + ":"); - pw.increaseIndent(); - } - pw.println("last location=" + mLastLocation.get(userId)); - pw.println("last coarse location=" + mLastCoarseLocation.get(userId)); - pw.println("enabled=" + isEnabled(userId)); - if (userIds.length != 1) { - pw.decreaseIndent(); - } - } - } - - mProvider.dump(fd, pw, args); - - pw.decreaseIndent(); - } - } - - class PassiveLocationProviderManager extends LocationProviderManager { - - private PassiveLocationProviderManager() { - super(PASSIVE_PROVIDER); - } - - @Override - public void setRealProvider(AbstractLocationProvider provider) { - Preconditions.checkArgument(provider instanceof PassiveProvider); - super.setRealProvider(provider); - } - - @Override - public void setMockProvider(@Nullable MockProvider provider) { - if (provider != null) { - throw new IllegalArgumentException("Cannot mock the passive provider"); - } - } - - public void updateLocation(Location location) { - synchronized (mLock) { - PassiveProvider passiveProvider = (PassiveProvider) mProvider.getProvider(); - Preconditions.checkState(passiveProvider != null); - - long identity = Binder.clearCallingIdentity(); - try { - passiveProvider.updateLocation(location); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - } - - /** - * A wrapper class holding either an ILocationListener or a PendingIntent to receive - * location updates. - */ - private final class Receiver extends LocationManagerServiceUtils.LinkedListenerBase implements - PendingIntent.OnFinished { - private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000; - - private final ILocationListener mListener; - final PendingIntent mPendingIntent; - final WorkSource mWorkSource; // WorkSource for battery blame, or null to assign to caller. - private final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver. - private final Object mKey; - - final HashMap<String, UpdateRecord> mUpdateRecords = new HashMap<>(); - - // True if app ops has started monitoring this receiver for locations. - private boolean mOpMonitoring; - // True if app ops has started monitoring this receiver for high power (gps) locations. - private boolean mOpHighPowerMonitoring; - private int mPendingBroadcasts; - PowerManager.WakeLock mWakeLock; - - private Receiver(ILocationListener listener, PendingIntent intent, CallerIdentity identity, - WorkSource workSource, boolean hideFromAppOps) { - super(identity); - mListener = listener; - mPendingIntent = intent; - if (listener != null) { - mKey = listener.asBinder(); - } else { - mKey = intent; - } - if (workSource != null && workSource.isEmpty()) { - workSource = null; - } - mWorkSource = workSource; - mHideFromAppOps = hideFromAppOps; - - updateMonitoring(true); - - // construct/configure wakelock - mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); - if (workSource == null) { - workSource = mCallerIdentity.addToWorkSource(null); - } - mWakeLock.setWorkSource(workSource); - - // For a non-reference counted wakelock, each acquire will reset the timeout, and we - // only need to release it once. - mWakeLock.setReferenceCounted(false); - } - - @Override - public boolean equals(Object otherObj) { - return (otherObj instanceof Receiver) && mKey.equals(((Receiver) otherObj).mKey); - } - - @Override - public int hashCode() { - return mKey.hashCode(); - } - - @Override - public String toString() { - StringBuilder s = new StringBuilder(); - s.append("Reciever["); - s.append(Integer.toHexString(System.identityHashCode(this))); - if (mListener != null) { - s.append(" listener"); - } else { - s.append(" intent"); - } - for (String p : mUpdateRecords.keySet()) { - s.append(" ").append(mUpdateRecords.get(p).toString()); - } - s.append(" monitoring location: ").append(mOpMonitoring); - s.append("]"); - return s.toString(); - } - - /** - * Update AppOp monitoring for this receiver. - * - * @param allow If true receiver is currently active, if false it's been removed. - */ - public void updateMonitoring(boolean allow) { - if (mHideFromAppOps) { - return; - } - - boolean requestingLocation = false; - boolean requestingHighPowerLocation = false; - if (allow) { - // See if receiver has any enabled update records. Also note if any update records - // are high power (has a high power provider with an interval under a threshold). - for (UpdateRecord updateRecord : mUpdateRecords.values()) { - LocationProviderManager manager = getLocationProviderManager( - updateRecord.mProvider); - if (manager == null) { - continue; - } - if (!manager.isEnabled(UserHandle.getUserId(mCallerIdentity.getUid())) - && !isSettingsExempt(updateRecord)) { - continue; - } - - requestingLocation = true; - ProviderProperties properties = manager.getProperties(); - if (properties != null - && properties.mPowerRequirement == Criteria.POWER_HIGH - && updateRecord.mRequest.getInterval() < HIGH_POWER_INTERVAL_MS) { - requestingHighPowerLocation = true; - break; - } - } - } - - // First update monitoring of any location request (including high power). - mOpMonitoring = updateMonitoring( - requestingLocation, - mOpMonitoring, - false); - - // Now update monitoring of high power requests only. - mOpHighPowerMonitoring = updateMonitoring( - requestingHighPowerLocation, - mOpHighPowerMonitoring, - true); - } - - private boolean updateMonitoring(boolean allowMonitoring, boolean currentlyMonitoring, - boolean highPower) { - if (!currentlyMonitoring) { - if (allowMonitoring) { - if (!highPower) { - return mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, mCallerIdentity); - } else { - return mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, - mCallerIdentity); - } - } - } else { - int permissionLevel = LocationPermissions.getPermissionLevel(mContext, - mCallerIdentity.getUid(), mCallerIdentity.getPid()); - if (!allowMonitoring || permissionLevel == PERMISSION_NONE - || !mAppOpsHelper.checkOpNoThrow( - LocationPermissions.asAppOp(permissionLevel), mCallerIdentity)) { - if (!highPower) { - mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, mCallerIdentity); - } else { - mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, mCallerIdentity); - } - return false; - } - } - - return currentlyMonitoring; - } - - public boolean isListener() { - return mListener != null; - } - - public boolean isPendingIntent() { - return mPendingIntent != null; - } - - public ILocationListener getListener() { - if (mListener != null) { - return mListener; - } - throw new IllegalStateException("Request for non-existent listener"); - } - - public boolean callLocationChangedLocked(Location location, - LocationRequest locationRequest) { - if (mListener != null) { - try { - mListener.onLocationChanged(new Location(location), new IRemoteCallback.Stub() { - @Override - public void sendResult(Bundle data) { - synchronized (mLock) { - decrementPendingBroadcastsLocked(); - } - } - }); - // call this after broadcasting so we do not increment - // if we throw an exception. - incrementPendingBroadcastsLocked(); - } catch (RemoteException e) { - return false; - } - } else { - Intent locationChanged = new Intent(); - locationChanged.putExtra(KEY_LOCATION_CHANGED, new Location(location)); - try { - mPendingIntent.send(mContext, 0, locationChanged, this, mHandler, - LocationPermissions.asPermission( - locationRequest.isCoarse() ? PERMISSION_COARSE - : PERMISSION_FINE), - PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); - // call this after broadcasting so we do not increment - // if we throw an exception. - incrementPendingBroadcastsLocked(); - } catch (PendingIntent.CanceledException e) { - return false; - } - } - return true; - } - - private boolean callProviderEnabledLocked(String provider, boolean enabled, - LocationRequest locationRequest) { - // First update AppOp monitoring. - // An app may get/lose location access as providers are enabled/disabled. - updateMonitoring(true); - - if (mListener != null) { - try { - mListener.onProviderEnabledChanged(provider, enabled); - } catch (RemoteException e) { - return false; - } - } else { - Intent providerIntent = new Intent(); - providerIntent.putExtra(KEY_PROVIDER_ENABLED, enabled); - try { - mPendingIntent.send(mContext, 0, providerIntent, null, mHandler, - LocationPermissions.asPermission( - locationRequest.isCoarse() ? PERMISSION_COARSE - : PERMISSION_FINE), - PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); - } catch (PendingIntent.CanceledException e) { - return false; - } - } - return true; - } - - @Override - public void binderDied() { - synchronized (mLock) { - removeUpdatesLocked(this); - clearPendingBroadcastsLocked(); - } - } - - @Override - public void onSendFinished(PendingIntent pendingIntent, Intent intent, - int resultCode, String resultData, Bundle resultExtras) { - synchronized (mLock) { - decrementPendingBroadcastsLocked(); - } - } - - // this must be called while synchronized by caller in a synchronized block - // containing the sending of the broadcaset - private void incrementPendingBroadcastsLocked() { - mPendingBroadcasts++; - // so wakelock calls will succeed - long identity = Binder.clearCallingIdentity(); - try { - mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS); - } finally { - Binder.restoreCallingIdentity(identity); - } - } + private void onLocationModeChanged(int userId) { + boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId); + LocationManager.invalidateLocalLocationEnabledCaches(); - private void decrementPendingBroadcastsLocked() { - if (--mPendingBroadcasts == 0) { - // so wakelock calls will succeed - long identity = Binder.clearCallingIdentity(); - try { - if (mWakeLock.isHeld()) { - mWakeLock.release(); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } + if (D) { + Log.d(TAG, "[u" + userId + "] location enabled = " + enabled); } - public void clearPendingBroadcastsLocked() { - if (mPendingBroadcasts > 0) { - mPendingBroadcasts = 0; - // so wakelock calls will succeed - long identity = Binder.clearCallingIdentity(); - try { - if (mWakeLock.isHeld()) { - mWakeLock.release(); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } + Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION) + .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); } @Override @@ -1459,17 +478,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @Nullable - LocationProviderManager getLocationProviderManager(String providerName) { - for (LocationProviderManager manager : mProviderManagers) { - if (providerName.equals(manager.getName())) { - return manager; - } - } - - return null; - } - @Override public List<String> getAllProviders() { ArrayList<String> providers = new ArrayList<>(mProviderManagers.size()); @@ -1532,397 +540,16 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - @GuardedBy("mLock") - private void updateProviderEnabledLocked(LocationProviderManager manager, boolean enabled) { - ArrayList<Receiver> deadReceivers = null; - ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName()); - if (records != null) { - for (UpdateRecord record : records) { - if (!mUserInfoHelper.isCurrentUserId( - UserHandle.getUserId(record.mReceiver.mCallerIdentity.getUid()))) { - continue; - } - - // requests that ignore location settings will never provide notifications - if (isSettingsExempt(record)) { - continue; - } - - // Sends a notification message to the receiver - if (!record.mReceiver.callProviderEnabledLocked(manager.getName(), enabled, - record.mRequest)) { - if (deadReceivers == null) { - deadReceivers = new ArrayList<>(); - } - deadReceivers.add(record.mReceiver); - } - } - } - - if (deadReceivers != null) { - for (int i = deadReceivers.size() - 1; i >= 0; i--) { - removeUpdatesLocked(deadReceivers.get(i)); - } - } - - applyRequirementsLocked(manager); - } - - @GuardedBy("mLock") - private void applyRequirementsLocked(String providerName) { - LocationProviderManager manager = getLocationProviderManager(providerName); - if (manager != null) { - applyRequirementsLocked(manager); - } - } - - @GuardedBy("mLock") - private void applyRequirementsLocked(LocationProviderManager manager) { - ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName()); - ProviderRequest.Builder providerRequest = new ProviderRequest.Builder(); - - // if provider is not active, it should not respond to requests - - if (mProviderManagers.contains(manager) && records != null && !records.isEmpty()) { - long backgroundThrottleInterval = mSettingsHelper.getBackgroundThrottleIntervalMs(); - - ArrayList<LocationRequest> requests = new ArrayList<>(records.size()); - - final boolean isForegroundOnlyMode = - mBatterySaverMode == PowerManager.LOCATION_MODE_FOREGROUND_ONLY; - final boolean shouldThrottleRequests = - mBatterySaverMode - == PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF - && !mPowerManager.isInteractive(); - // initialize the low power mode to true and set to false if any of the records requires - providerRequest.setLowPowerMode(true); - for (UpdateRecord record : records) { - CallerIdentity identity = record.mReceiver.mCallerIdentity; - if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) { - continue; - } - - if (!mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp( - record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE), - identity)) { - continue; - } - final boolean isBatterySaverDisablingLocation = shouldThrottleRequests - || (isForegroundOnlyMode && !record.mIsForegroundUid); - if (!manager.isEnabled(identity.getUserId()) || isBatterySaverDisablingLocation) { - if (isSettingsExempt(record)) { - providerRequest.setLocationSettingsIgnored(true); - providerRequest.setLowPowerMode(false); - } else { - continue; - } - } - - LocationRequest locationRequest = record.mRealRequest; - long interval = locationRequest.getInterval(); - - - // if we're forcing location, don't apply any throttling - if (!providerRequest.isLocationSettingsIgnored() && !isThrottlingExempt( - record.mReceiver.mCallerIdentity)) { - if (!record.mIsForegroundUid) { - interval = Math.max(interval, backgroundThrottleInterval); - } - if (interval != locationRequest.getInterval()) { - locationRequest = new LocationRequest(locationRequest); - locationRequest.setInterval(interval); - } - } - - record.mRequest = locationRequest; - requests.add(locationRequest); - if (!locationRequest.isLowPowerMode()) { - providerRequest.setLowPowerMode(false); - } - if (interval < providerRequest.getInterval()) { - providerRequest.setInterval(interval); - } - } - - providerRequest.setLocationRequests(requests); - - if (providerRequest.getInterval() < Long.MAX_VALUE) { - // calculate who to blame for power - // This is somewhat arbitrary. We pick a threshold interval - // that is slightly higher that the minimum interval, and - // spread the blame across all applications with a request - // under that threshold. - // TODO: overflow - long thresholdInterval = (providerRequest.getInterval() + 1000) * 3 / 2; - for (UpdateRecord record : records) { - if (mUserInfoHelper.isCurrentUserId( - UserHandle.getUserId(record.mReceiver.mCallerIdentity.getUid()))) { - LocationRequest locationRequest = record.mRequest; - - // Don't assign battery blame for update records whose - // client has no permission to receive location data. - if (!providerRequest.getLocationRequests().contains(locationRequest)) { - continue; - } - - if (locationRequest.getInterval() <= thresholdInterval) { - if (record.mReceiver.mWorkSource != null - && isValidWorkSource(record.mReceiver.mWorkSource)) { - providerRequest.getWorkSource().add(record.mReceiver.mWorkSource); - } else { - // Assign blame to caller if there's no WorkSource associated with - // the request or if it's invalid. - providerRequest.getWorkSource().add( - record.mReceiver.mCallerIdentity.getUid(), - record.mReceiver.mCallerIdentity.getPackageName()); - } - } - } - } - } - } - - manager.setRequest(providerRequest.build()); - } - - /** - * Whether a given {@code WorkSource} associated with a Location request is valid. - */ - private static boolean isValidWorkSource(WorkSource workSource) { - if (workSource.size() > 0) { - // If the WorkSource has one or more non-chained UIDs, make sure they're accompanied - // by tags. - return workSource.getPackageName(0) != null; - } else { - // For now, make sure callers have supplied an attribution tag for use with - // AppOpsManager. This might be relaxed in the future. - final List<WorkChain> workChains = workSource.getWorkChains(); - return workChains != null && !workChains.isEmpty() - && workChains.get(0).getAttributionTag() != null; - } - } - @Override public String[] getBackgroundThrottlingWhitelist() { - return mSettingsHelper.getBackgroundThrottlePackageWhitelist().toArray(new String[0]); + return mInjector.getSettingsHelper().getBackgroundThrottlePackageWhitelist().toArray( + new String[0]); } @Override public String[] getIgnoreSettingsWhitelist() { - return mSettingsHelper.getIgnoreSettingsPackageWhitelist().toArray(new String[0]); - } - - private boolean isThrottlingExempt(CallerIdentity callerIdentity) { - if (callerIdentity.getUid() == Process.SYSTEM_UID) { - return true; - } - - if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains( - callerIdentity.getPackageName())) { - return true; - } - - return mLocalService.isProvider(null, callerIdentity); - - } - - private boolean isSettingsExempt(UpdateRecord record) { - if (!record.mRealRequest.isLocationSettingsIgnored()) { - return false; - } - - if (mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains( - record.mReceiver.mCallerIdentity.getPackageName())) { - return true; - } - - return mLocalService.isProvider(null, record.mReceiver.mCallerIdentity); - } - - private class UpdateRecord { - final String mProvider; - private final LocationRequest mRealRequest; // original request from client - LocationRequest mRequest; // possibly throttled version of the request - private final Receiver mReceiver; - private boolean mIsForegroundUid; - private Location mLastFixBroadcast; - private Throwable mStackTrace; // for debugging only - private long mExpirationRealtimeMs; - - /** - * Note: must be constructed with lock held. - */ - private UpdateRecord(String provider, LocationRequest request, Receiver receiver) { - if (Build.IS_DEBUGGABLE) { - Preconditions.checkState(Thread.holdsLock(mLock)); - } - mExpirationRealtimeMs = request.getExpirationRealtimeMs(SystemClock.elapsedRealtime()); - mProvider = provider; - mRealRequest = request; - mRequest = request; - mReceiver = receiver; - mIsForegroundUid = mAppForegroundHelper.isAppForeground( - mReceiver.mCallerIdentity.getUid()); - - if (D && receiver.mCallerIdentity.getPid() == Process.myPid()) { - mStackTrace = new Throwable(); - } - - ArrayList<UpdateRecord> records = mRecordsByProvider.computeIfAbsent(provider, - k -> new ArrayList<>()); - if (!records.contains(this)) { - records.add(this); - } - - // Update statistics for historical location requests by package/provider - mRequestStatistics.startRequesting( - mReceiver.mCallerIdentity.getPackageName(), - mReceiver.mCallerIdentity.getAttributionTag(), - provider, request.getInterval(), mIsForegroundUid); - } - - /** - * Method to be called when record changes foreground/background - */ - private void updateForeground(boolean isForeground) { - mIsForegroundUid = isForeground; - mRequestStatistics.updateForeground( - mReceiver.mCallerIdentity.getPackageName(), - mReceiver.mCallerIdentity.getAttributionTag(), - mProvider, isForeground); - } - - /** - * Method to be called when a record will no longer be used. - */ - private void disposeLocked(boolean removeReceiver) { - if (Build.IS_DEBUGGABLE) { - Preconditions.checkState(Thread.holdsLock(mLock)); - } - - CallerIdentity identity = mReceiver.mCallerIdentity; - mRequestStatistics.stopRequesting(identity.getPackageName(), - identity.getAttributionTag(), - mProvider); - - mLocationUsageLogger.logLocationApiUsage( - LocationStatsEnums.USAGE_ENDED, - LocationStatsEnums.API_REQUEST_LOCATION_UPDATES, - identity.getPackageName(), - mRealRequest, - mReceiver.isListener(), - mReceiver.isPendingIntent(), - /* geofence= */ null, - mAppForegroundHelper.isAppForeground(mReceiver.mCallerIdentity.getUid())); - - // remove from mRecordsByProvider - ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider); - if (globalRecords != null) { - globalRecords.remove(this); - } - - if (!removeReceiver) return; // the caller will handle the rest - - // remove from Receiver#mUpdateRecords - HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords; - receiverRecords.remove(this.mProvider); - - // and also remove the Receiver if it has no more update records - if (receiverRecords.size() == 0) { - removeUpdatesLocked(mReceiver); - } - } - - @Override - public String toString() { - StringBuilder b = new StringBuilder("UpdateRecord["); - b.append(mProvider).append(" "); - b.append(mReceiver.mCallerIdentity).append(" "); - if (!mIsForegroundUid) { - b.append("(background) "); - } - b.append(mRealRequest).append(" ").append(mReceiver.mWorkSource); - - if (mStackTrace != null) { - ByteArrayOutputStream tmp = new ByteArrayOutputStream(); - mStackTrace.printStackTrace(new PrintStream(tmp)); - b.append("\n\n").append(tmp.toString()).append("\n"); - } - - b.append("]"); - return b.toString(); - } - } - - @GuardedBy("mLock") - private Receiver getReceiverLocked(ILocationListener listener, CallerIdentity identity, - WorkSource workSource, boolean hideFromAppOps) { - IBinder binder = listener.asBinder(); - Receiver receiver = mReceivers.get(binder); - if (receiver == null && identity != null) { - receiver = new Receiver(listener, null, identity, workSource, - hideFromAppOps); - if (!receiver.linkToListenerDeathNotificationLocked( - receiver.getListener().asBinder())) { - return null; - } - mReceivers.put(binder, receiver); - } - return receiver; - } - - @GuardedBy("mLock") - private Receiver getReceiverLocked(PendingIntent intent, CallerIdentity identity, - WorkSource workSource, boolean hideFromAppOps) { - Receiver receiver = mReceivers.get(intent); - if (receiver == null && identity != null) { - receiver = new Receiver(null, intent, identity, workSource, - hideFromAppOps); - mReceivers.put(intent, receiver); - } - return receiver; - } - - /** - * Creates a LocationRequest based upon the supplied LocationRequest that to meets resolution - * and consistency requirements. - * - * @param request the LocationRequest from which to create a sanitized version - * @return a version of request that meets the given resolution and consistency requirements - * @hide - */ - private LocationRequest createSanitizedRequest(LocationRequest request, - boolean callerHasLocationHardwarePermission, int permissionLevel) { - LocationRequest sanitizedRequest = new LocationRequest(request); - if (!callerHasLocationHardwarePermission) { - // allow setting low power mode only for callers with location hardware permission - sanitizedRequest.setLowPowerMode(false); - } - if (permissionLevel < PERMISSION_FINE) { - sanitizedRequest.setCoarse(true); - switch (sanitizedRequest.getQuality()) { - case LocationRequest.ACCURACY_FINE: - sanitizedRequest.setQuality(LocationRequest.ACCURACY_BLOCK); - break; - case LocationRequest.POWER_HIGH: - sanitizedRequest.setQuality(LocationRequest.POWER_LOW); - break; - } - // throttle - if (sanitizedRequest.getInterval() < FASTEST_COARSE_INTERVAL_MS) { - sanitizedRequest.setInterval(FASTEST_COARSE_INTERVAL_MS); - } - if (sanitizedRequest.getFastestInterval() < FASTEST_COARSE_INTERVAL_MS) { - sanitizedRequest.setFastestInterval(FASTEST_COARSE_INTERVAL_MS); - } - } else { - sanitizedRequest.setCoarse(false); - } - // make getFastestInterval() the minimum of interval and fastest interval - if (sanitizedRequest.getFastestInterval() > sanitizedRequest.getInterval()) { - sanitizedRequest.setFastestInterval(request.getInterval()); - } - return sanitizedRequest; + return mInjector.getSettingsHelper().getIgnoreSettingsPackageWhitelist().toArray( + new String[0]); } @Override @@ -1930,45 +557,24 @@ public class LocationManagerService extends ILocationManager.Stub { String packageName, String attributionTag, String listenerId) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, listenerId); - int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext); - LocationPermissions.enforceLocationPermission(Binder.getCallingUid(), permissionLevel, + int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), + identity.getPid()); + LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel, PERMISSION_COARSE); - WorkSource workSource = request.getWorkSource(); - if (workSource != null && !workSource.isEmpty()) { - mContext.enforceCallingOrSelfPermission( - permission.UPDATE_DEVICE_STATS, null); + // clients in the system process should have an attribution tag set + if (identity.getPid() == Process.myPid() && attributionTag == null) { + Log.w(TAG, "system location request with no attribution tag", + new IllegalArgumentException()); } - boolean hideFromAppOps = request.getHideFromAppOps(); - if (hideFromAppOps) { - mContext.enforceCallingOrSelfPermission( - permission.UPDATE_APP_OPS_STATS, null); - } - if (request.isLocationSettingsIgnored()) { - mContext.enforceCallingOrSelfPermission( - permission.WRITE_SECURE_SETTINGS, null); - } - boolean callerHasLocationHardwarePermission = - mContext.checkCallingPermission(permission.LOCATION_HARDWARE) - == PERMISSION_GRANTED; - LocationRequest sanitizedRequest = createSanitizedRequest(request, - callerHasLocationHardwarePermission, - permissionLevel); - mLocationUsageLogger.logLocationApiUsage( - LocationStatsEnums.USAGE_STARTED, - LocationStatsEnums.API_REQUEST_LOCATION_UPDATES, - packageName, request, true, false, - /* geofence= */ null, - mAppForegroundHelper.isAppForeground(identity.getUid())); + request = validateAndSanitizeLocationRequest(request, permissionLevel); - synchronized (mLock) { - Receiver receiver = getReceiverLocked(Objects.requireNonNull(listener), identity, - workSource, hideFromAppOps); - if (receiver != null) { - requestLocationUpdatesLocked(sanitizedRequest, receiver); - } - } + LocationProviderManager manager = getLocationProviderManager(request.getProvider()); + Preconditions.checkArgument(manager != null, + "provider \"" + request.getProvider() + "\" does not exist"); + + manager.registerLocationRequest(request, identity, permissionLevel, listener); } @Override @@ -1976,248 +582,154 @@ public class LocationManagerService extends ILocationManager.Stub { String packageName, String attributionTag) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, AppOpsManager.toReceiverId(pendingIntent)); - int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext); - LocationPermissions.enforceLocationPermission(Binder.getCallingUid(), permissionLevel, + int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), + identity.getPid()); + LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel, PERMISSION_COARSE); + // clients in the system process must have an attribution tag set + Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null); + + request = validateAndSanitizeLocationRequest(request, permissionLevel); + + LocationProviderManager manager = getLocationProviderManager(request.getProvider()); + Preconditions.checkArgument(manager != null, + "provider \"" + request.getProvider() + "\" does not exist"); + + manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent); + } + + private LocationRequest validateAndSanitizeLocationRequest(LocationRequest request, + @PermissionLevel int permissionLevel) { + Objects.requireNonNull(request.getProvider()); + WorkSource workSource = request.getWorkSource(); if (workSource != null && !workSource.isEmpty()) { mContext.enforceCallingOrSelfPermission( - permission.UPDATE_DEVICE_STATS, null); + permission.UPDATE_DEVICE_STATS, + "setting a work source requires " + permission.UPDATE_DEVICE_STATS); } - boolean hideFromAppOps = request.getHideFromAppOps(); - if (hideFromAppOps) { + if (request.getHideFromAppOps()) { mContext.enforceCallingOrSelfPermission( - permission.UPDATE_APP_OPS_STATS, null); + permission.UPDATE_APP_OPS_STATS, + "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS); } if (request.isLocationSettingsIgnored()) { mContext.enforceCallingOrSelfPermission( - permission.WRITE_SECURE_SETTINGS, null); + permission.WRITE_SECURE_SETTINGS, + "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS); } - boolean callerHasLocationHardwarePermission = - mContext.checkCallingPermission(permission.LOCATION_HARDWARE) - == PERMISSION_GRANTED; - LocationRequest sanitizedRequest = createSanitizedRequest(request, - callerHasLocationHardwarePermission, - permissionLevel); - - mLocationUsageLogger.logLocationApiUsage( - LocationStatsEnums.USAGE_STARTED, - LocationStatsEnums.API_REQUEST_LOCATION_UPDATES, - packageName, request, true, false, - /* geofence= */ null, - mAppForegroundHelper.isAppForeground(identity.getUid())); - synchronized (mLock) { - Receiver receiver = getReceiverLocked(Objects.requireNonNull(pendingIntent), identity, - workSource, hideFromAppOps); - if (receiver != null) { - requestLocationUpdatesLocked(sanitizedRequest, receiver); - } + LocationRequest sanitized = new LocationRequest(request); + if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE) != PERMISSION_GRANTED) { + sanitized.setLowPowerMode(false); } - } - - @GuardedBy("mLock") - private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver) { - String provider = request.getProvider(); + if (permissionLevel < PERMISSION_FINE) { + switch (sanitized.getQuality()) { + case LocationRequest.ACCURACY_FINE: + sanitized.setQuality(LocationRequest.ACCURACY_BLOCK); + break; + case LocationRequest.POWER_HIGH: + sanitized.setQuality(LocationRequest.POWER_LOW); + break; + } - LocationProviderManager manager = getLocationProviderManager(provider); - if (manager == null) { - throw new IllegalArgumentException("provider doesn't exist: " + provider); + if (sanitized.getInterval() < FASTEST_COARSE_INTERVAL_MS) { + sanitized.setInterval(FASTEST_COARSE_INTERVAL_MS); + } + if (sanitized.getFastestInterval() < FASTEST_COARSE_INTERVAL_MS) { + sanitized.setFastestInterval(FASTEST_COARSE_INTERVAL_MS); + } } - - UpdateRecord record = new UpdateRecord(provider, request, receiver); - - UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, record); - if (oldRecord != null) { - oldRecord.disposeLocked(false); + if (sanitized.getFastestInterval() > sanitized.getInterval()) { + sanitized.setFastestInterval(request.getInterval()); } - - long identity = Binder.clearCallingIdentity(); - try { - int userId = UserHandle.getUserId(receiver.mCallerIdentity.getUid()); - if (!manager.isEnabled(userId) && !isSettingsExempt(record)) { - // Notify the listener that updates are currently disabled - but only if the request - // does not ignore location settings - receiver.callProviderEnabledLocked(provider, false, request); + if (sanitized.getWorkSource() != null) { + if (sanitized.getWorkSource().isEmpty()) { + sanitized.setWorkSource(null); + } else if (sanitized.getWorkSource().getPackageName(0) == null) { + Log.w(TAG, "received (and ignoring) illegal worksource with no package name"); + sanitized.setWorkSource(null); + } else { + List<WorkChain> workChains = sanitized.getWorkSource().getWorkChains(); + if (workChains != null && !workChains.isEmpty() && workChains.get( + 0).getAttributionTag() == null) { + Log.w(TAG, + "received (and ignoring) illegal worksource with no attribution tag"); + sanitized.setWorkSource(null); + } } - - applyRequirementsLocked(provider); - - // Update the monitoring here just in case multiple location requests were added to the - // same receiver (this request may be high power and the initial might not have been). - receiver.updateMonitoring(true); - } finally { - Binder.restoreCallingIdentity(identity); } + + return sanitized; } @Override public void unregisterLocationListener(ILocationListener listener) { - synchronized (mLock) { - Receiver receiver = getReceiverLocked(Objects.requireNonNull(listener), null, null, - false); - if (receiver != null) { - removeUpdatesLocked(receiver); - } + for (LocationProviderManager manager : mProviderManagers) { + manager.unregisterLocationRequest(listener); } } @Override public void unregisterLocationPendingIntent(PendingIntent pendingIntent) { - synchronized (mLock) { - Receiver receiver = getReceiverLocked(Objects.requireNonNull(pendingIntent), null, null, - false); - if (receiver != null) { - removeUpdatesLocked(receiver); - } - } - } - - @GuardedBy("mLock") - private void removeUpdatesLocked(Receiver receiver) { - if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver))); - - if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) { - receiver.unlinkFromListenerDeathNotificationLocked( - receiver.getListener().asBinder()); - receiver.clearPendingBroadcastsLocked(); - } - - receiver.updateMonitoring(false); - - // Record which providers were associated with this listener - HashSet<String> providers = new HashSet<>(); - HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords; - if (oldRecords != null) { - // Call dispose() on the obsolete update records. - for (UpdateRecord record : oldRecords.values()) { - // Update statistics for historical location requests by package/provider - record.disposeLocked(false); - } - // Accumulate providers - providers.addAll(oldRecords.keySet()); - } - - // update provider - for (String provider : providers) { - applyRequirementsLocked(provider); + for (LocationProviderManager manager : mProviderManagers) { + manager.unregisterLocationRequest(pendingIntent); } } @Override public Location getLastLocation(LocationRequest request, String packageName, String attributionTag) { - // unsafe is ok because app ops will verify the package name - CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag); - int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext); - LocationPermissions.enforceLocationPermission(Binder.getCallingUid(), permissionLevel, + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); + int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), + identity.getPid()); + LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel, PERMISSION_COARSE); - if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), - identity.getPackageName())) { - return null; - } - if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) { - return null; - } + // clients in the system process must have an attribution tag set + Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null); - synchronized (mLock) { - LocationProviderManager manager = getLocationProviderManager(request.getProvider()); - if (manager == null) { - return null; - } - if (!manager.isEnabled(identity.getUserId()) && !request.isLocationSettingsIgnored()) { - return null; - } + request = validateAndSanitizeLocationRequest(request, permissionLevel); - // appops check should always be right before delivery - if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), - identity)) { - return null; - } + LocationProviderManager manager = getLocationProviderManager(request.getProvider()); + if (manager == null) { + return null; + } - Location location = manager.getLastLocation(identity.getUserId(), permissionLevel); + Location location = manager.getLastLocation(request, identity, permissionLevel); - // make a defensive copy - the client could be in the same process as us - return location != null ? new Location(location) : null; + // lastly - note app ops + if (!mInjector.getAppOpsHelper().noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), + identity)) { + return null; } + + return location; } @Override public void getCurrentLocation(LocationRequest request, - ICancellationSignal remoteCancellationSignal, ILocationCallback callback, + ICancellationSignal cancellationTransport, ILocationCallback consumer, String packageName, String attributionTag, String listenerId) { - // unsafe is ok because app ops will verify the package name - CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag, + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, listenerId); - int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext); - LocationPermissions.enforceLocationPermission(Binder.getCallingUid(), permissionLevel, + int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), + identity.getPid()); + LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel, PERMISSION_COARSE); - request = createSanitizedRequest(request, false, permissionLevel); - request.setNumUpdates(1); - if (request.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) { - request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS); - } - - GetCurrentLocationTransport transport = new GetCurrentLocationTransport(callback); - - if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), - identity.getPackageName())) { - transport.deliverResult(null); - return; - } - if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) { - transport.deliverResult(null); - return; - } - - Location lastLocation; - synchronized (mLock) { - LocationProviderManager manager = getLocationProviderManager(request.getProvider()); - if (manager == null) { - transport.deliverResult(null); - return; - } - if (!manager.isEnabled(identity.getUserId()) && !request.isLocationSettingsIgnored()) { - transport.deliverResult(null); - return; - } - - lastLocation = manager.getLastLocation(identity.getUserId(), permissionLevel); - } - - if (lastLocation != null) { - long locationAgeMs = NANOSECONDS.toMillis( - SystemClock.elapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos()); + // clients in the system process must have an attribution tag set + Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null); - if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) { - // appops check should always be right before delivery - if (mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), - identity)) { - transport.deliverResult(lastLocation); - } else { - transport.deliverResult(null); - } - return; - } + request = validateAndSanitizeLocationRequest(request, permissionLevel); - if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid())) { - if (locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) { - // not allowed to request new locations, so we can't return anything - transport.deliverResult(null); - return; - } - } - } + LocationProviderManager manager = getLocationProviderManager(request.getProvider()); + Preconditions.checkArgument(manager != null, + "provider \"" + request.getProvider() + "\" does not exist"); - registerLocationListener(request, transport, packageName, attributionTag, listenerId); - CancellationSignal cancellationSignal = CancellationSignal.fromTransport( - remoteCancellationSignal); - if (cancellationSignal != null) { - cancellationSignal.setOnCancelListener(() -> unregisterLocationListener(transport)); - } + manager.getCurrentLocation(request, identity, permissionLevel, cancellationTransport, + consumer); } @Override @@ -2228,8 +740,13 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - Location location = gpsManager.getLastLocation(UserHandle.getCallingUserId(), - PERMISSION_FINE); + // create a location request that works in almost all circumstances + LocationRequest request = LocationRequest.createFromDeprecatedProvider(GPS_PROVIDER, 0, + 0, true); + + // use our own identity rather than the caller + CallerIdentity identity = CallerIdentity.fromContext(mContext); + Location location = gpsManager.getLastLocation(request, identity, PERMISSION_FINE); if (location == null) { return null; } @@ -2248,11 +765,9 @@ public class LocationManagerService extends ILocationManager.Stub { Preconditions.checkArgument(location.isComplete()); int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - LocationProviderManager manager = getLocationProviderManager(location.getProvider()); - if (manager != null && manager.isEnabled(userId)) { - manager.injectLastLocation(Objects.requireNonNull(location), userId); - } + LocationProviderManager manager = getLocationProviderManager(location.getProvider()); + if (manager != null && manager.isEnabled(userId)) { + manager.injectLastLocation(Objects.requireNonNull(location), userId); } } @@ -2357,12 +872,11 @@ public class LocationManagerService extends ILocationManager.Stub { Objects.requireNonNull(command), extras); } - mLocationUsageLogger.logLocationApiUsage( + mInjector.getLocationUsageLogger().logLocationApiUsage( LocationStatsEnums.USAGE_STARTED, LocationStatsEnums.API_SEND_EXTRA_COMMAND, provider); - - mLocationUsageLogger.logLocationApiUsage( + mInjector.getLocationUsageLogger().logLocationApiUsage( LocationStatsEnums.USAGE_ENDED, LocationStatsEnums.API_SEND_EXTRA_COMMAND, provider); @@ -2385,7 +899,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (provider != null && !provider.equals(manager.getName())) { continue; } - CallerIdentity identity = manager.getProviderIdentity(); + CallerIdentity identity = manager.getIdentity(); if (identity == null) { continue; } @@ -2406,7 +920,7 @@ public class LocationManagerService extends ILocationManager.Stub { return Collections.emptyList(); } - CallerIdentity identity = manager.getProviderIdentity(); + CallerIdentity identity = manager.getIdentity(); if (identity == null) { return Collections.emptyList(); } @@ -2454,164 +968,26 @@ public class LocationManagerService extends ILocationManager.Stub { mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null); - invalidateLocalLocationEnabledCaches(); - mSettingsHelper.setLocationEnabled(enabled, userId); + LocationManager.invalidateLocalLocationEnabledCaches(); + mInjector.getSettingsHelper().setLocationEnabled(enabled, userId); } @Override public boolean isLocationEnabledForUser(int userId) { userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, "isLocationEnabledForUser", null); - return mSettingsHelper.isLocationEnabled(userId); + return mInjector.getSettingsHelper().isLocationEnabled(userId); } @Override public boolean isProviderEnabledForUser(String provider, int userId) { - // Fused provider is accessed indirectly via criteria rather than the provider-based APIs, + // fused provider is accessed indirectly via criteria rather than the provider-based APIs, // so we discourage its use if (FUSED_PROVIDER.equals(provider)) return false; return mLocalService.isProviderEnabledForUser(provider, userId); } - @GuardedBy("mLock") - private static boolean shouldBroadcastSafeLocked( - Location loc, Location lastLoc, UpdateRecord record, long now) { - // Always broadcast the first update - if (lastLoc == null) { - return true; - } - - // Check whether sufficient time has passed - long minTime = record.mRealRequest.getFastestInterval(); - long deltaMs = NANOSECONDS.toMillis( - loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos()); - if (deltaMs < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) { - return false; - } - - // Check whether sufficient distance has been traveled - double minDistance = record.mRealRequest.getSmallestDisplacement(); - if (minDistance > 0.0) { - if (loc.distanceTo(lastLoc) <= minDistance) { - return false; - } - } - - // Check whether sufficient number of udpates is left - if (record.mRealRequest.getNumUpdates() <= 0) { - return false; - } - - // Check whether the expiry date has passed - return record.mExpirationRealtimeMs >= now; - } - - @GuardedBy("mLock") - private void handleLocationChangedLocked(LocationProviderManager manager, Location fineLocation, - Location coarseLocation) { - if (!mProviderManagers.contains(manager)) { - Log.w(TAG, "received location from unknown provider: " + manager.getName()); - return; - } - - // notify passive provider - if (manager != mPassiveManager) { - mPassiveManager.updateLocation(fineLocation); - } - - long now = SystemClock.elapsedRealtime(); - - ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName()); - if (records == null || records.size() == 0) return; - - ArrayList<Receiver> deadReceivers = null; - ArrayList<UpdateRecord> deadUpdateRecords = null; - - // Broadcast location to all listeners - for (UpdateRecord r : records) { - Receiver receiver = r.mReceiver; - CallerIdentity identity = receiver.mCallerIdentity; - boolean receiverDead = false; - - - if (!manager.isEnabled(identity.getUserId()) && !isSettingsExempt(r)) { - continue; - } - - if (!mUserInfoHelper.isCurrentUserId(identity.getUserId()) - && !mLocalService.isProvider(null, identity)) { - continue; - } - - if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), - identity.getPackageName())) { - continue; - } - - int permissionLevel = r.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE; - - Location location; - switch (permissionLevel) { - case PERMISSION_COARSE: - location = coarseLocation; - break; - case PERMISSION_FINE: - location = fineLocation; - break; - default: - throw new AssertionError(); - } - - if (shouldBroadcastSafeLocked(location, r.mLastFixBroadcast, r, now)) { - r.mLastFixBroadcast = location; - - // appops check should always be right before delivery - if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), - receiver.mCallerIdentity)) { - continue; - } - - if (!receiver.callLocationChangedLocked(location, r.mRequest)) { - receiverDead = true; - } - r.mRealRequest.decrementNumUpdates(); - } - - // track expired records - if (r.mRealRequest.getNumUpdates() <= 0 || r.mExpirationRealtimeMs < now) { - if (deadUpdateRecords == null) { - deadUpdateRecords = new ArrayList<>(); - } - deadUpdateRecords.add(r); - } - // track dead receivers - if (receiverDead) { - if (deadReceivers == null) { - deadReceivers = new ArrayList<>(); - } - if (!deadReceivers.contains(receiver)) { - deadReceivers.add(receiver); - } - } - } - - // remove dead records and receivers outside the loop - if (deadReceivers != null) { - for (Receiver receiver : deadReceivers) { - removeUpdatesLocked(receiver); - } - } - if (deadUpdateRecords != null) { - for (UpdateRecord r : deadUpdateRecords) { - r.disposeLocked(true); - } - applyRequirementsLocked(manager); - } - } - - // Geocoder - @Override public boolean geocoderIsPresent() { return mGeocodeProvider != null; @@ -2637,7 +1013,6 @@ public class LocationManagerService extends ILocationManager.Stub { double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude, int maxResults, GeocoderParams params, IGeocodeListener listener) { - if (mGeocodeProvider != null) { mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude, @@ -2651,35 +1026,24 @@ public class LocationManagerService extends ILocationManager.Stub { } } - // Mock Providers - @Override public void addTestProvider(String provider, ProviderProperties properties, String packageName, String attributionTag) { // unsafe is ok because app ops will verify the package name - CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, - attributionTag); - if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) { + CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag); + if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) { return; } - synchronized (mLock) { - LocationProviderManager manager = getLocationProviderManager(provider); - if (manager == null) { - manager = new LocationProviderManager(provider); - mProviderManagers.add(manager); - } - - manager.setMockProvider(new MockProvider(properties, identity)); - } + getOrAddLocationProviderManager(provider).setMockProvider( + new MockProvider(properties, identity)); } @Override public void removeTestProvider(String provider, String packageName, String attributionTag) { // unsafe is ok because app ops will verify the package name - CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, - attributionTag); - if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) { + CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag); + if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) { return; } @@ -2691,7 +1055,7 @@ public class LocationManagerService extends ILocationManager.Stub { manager.setMockProvider(null); if (!manager.hasProvider()) { - mProviderManagers.remove(manager); + removeLocationProviderManager(manager); } } } @@ -2702,7 +1066,7 @@ public class LocationManagerService extends ILocationManager.Stub { // unsafe is ok because app ops will verify the package name CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag); - if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) { + if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) { return; } @@ -2723,7 +1087,7 @@ public class LocationManagerService extends ILocationManager.Stub { // unsafe is ok because app ops will verify the package name CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag); - if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) { + if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) { return; } @@ -2777,57 +1141,32 @@ public class LocationManagerService extends ILocationManager.Stub { ipw.println("User Info:"); ipw.increaseIndent(); - mUserInfoHelper.dump(fd, ipw, args); + mInjector.getUserInfoHelper().dump(fd, ipw, args); ipw.decreaseIndent(); ipw.println("Location Settings:"); ipw.increaseIndent(); - mSettingsHelper.dump(fd, ipw, args); + mInjector.getSettingsHelper().dump(fd, ipw, args); ipw.decreaseIndent(); - synchronized (mLock) { - ipw.println("Battery Saver Location Mode: " - + locationPowerSaveModeToString(mBatterySaverMode)); - - if (dumpFilter == null) { - ipw.println("Location Listeners:"); - ipw.increaseIndent(); - for (Receiver receiver : mReceivers.values()) { - ipw.println(receiver); - } - ipw.decreaseIndent(); - - ipw.println("Active Records by Provider:"); - ipw.increaseIndent(); - for (Map.Entry<String, ArrayList<UpdateRecord>> entry : - mRecordsByProvider.entrySet()) { - ipw.println(entry.getKey() + ":"); - ipw.increaseIndent(); - for (UpdateRecord record : entry.getValue()) { - ipw.println(record); - } - ipw.decreaseIndent(); - } - ipw.decreaseIndent(); - - ipw.println("Historical Records by Provider:"); - ipw.increaseIndent(); - TreeMap<PackageProviderKey, PackageStatistics> sorted = new TreeMap<>( - mRequestStatistics.statistics); - for (Map.Entry<PackageProviderKey, PackageStatistics> entry - : sorted.entrySet()) { - ipw.println(entry.getKey() + ": " + entry.getValue()); - } - ipw.decreaseIndent(); + ipw.println("Historical Records by Provider:"); + ipw.increaseIndent(); + TreeMap<PackageProviderKey, PackageStatistics> sorted = new TreeMap<>( + mInjector.getLocationRequestStatistics().statistics); + for (Map.Entry<PackageProviderKey, PackageStatistics> entry + : sorted.entrySet()) { + ipw.println(entry.getKey() + ": " + entry.getValue()); + } + ipw.decreaseIndent(); - mRequestStatistics.history.dump(ipw); + mInjector.getLocationRequestStatistics().history.dump(ipw); - if (mExtraLocationControllerPackage != null) { - ipw.println( - "Location Controller Extra Package: " + mExtraLocationControllerPackage - + (mExtraLocationControllerPackageEnabled ? " [enabled]" - : "[disabled]")); - } + synchronized (mLock) { + if (mExtraLocationControllerPackage != null) { + ipw.println( + "Location Controller Extra Package: " + mExtraLocationControllerPackage + + (mExtraLocationControllerPackageEnabled ? " [enabled]" + : "[disabled]")); } } @@ -2857,47 +1196,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private class GetCurrentLocationTransport extends ILocationListener.Stub { - - private final Executor mExecutor; - private final ILocationCallback mCallback; - - GetCurrentLocationTransport(ILocationCallback callback) { - mExecutor = FgThread.getExecutor(); - mCallback = callback; - } - - @Override - public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) { - mExecutor.execute(() -> { - deliverResult(location); - try { - onCompleteCallback.sendResult(null); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - }); - unregisterLocationListener(this); - } - - @Override - public void onProviderEnabledChanged(String provider, boolean enabled) - throws RemoteException { - if (!enabled) { - deliverResult(null); - unregisterLocationListener(this); - } - } - - public void deliverResult(@Nullable Location location) { - try { - mCallback.onLocation(location); - } catch (RemoteException e) { - // do nothing - } - } - } - private class LocalService extends LocationManagerInternal { LocalService() {} @@ -2916,17 +1214,28 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override + public void addProviderEnabledListener(String provider, ProviderEnabledListener listener) { + LocationProviderManager manager = Objects.requireNonNull( + getLocationProviderManager(provider)); + manager.addEnabledListener(listener); + } + + @Override + public void removeProviderEnabledListener(String provider, + ProviderEnabledListener listener) { + LocationProviderManager manager = Objects.requireNonNull( + getLocationProviderManager(provider)); + manager.removeEnabledListener(listener); + } + + @Override public boolean isProvider(String provider, CallerIdentity identity) { - for (LocationProviderManager manager : mProviderManagers) { - if (provider != null && !provider.equals(manager.getName())) { - continue; - } - if (identity.equals(manager.getProviderIdentity())) { - return true; - } + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + return false; + } else { + return identity.equals(manager.getIdentity()); } - - return false; } @Override @@ -2935,6 +1244,13 @@ public class LocationManagerService extends ILocationManager.Stub { mGnssManagerService.sendNiResponse(notifId, userResponse); } } + + @Override + public void reportGnssBatchLocations(List<Location> locations) { + if (mGnssManagerService != null) { + mGnssManagerService.onReportLocation(locations); + } + } } private static class SystemInjector implements Injector { diff --git a/services/core/java/com/android/server/location/LocationManagerServiceUtils.java b/services/core/java/com/android/server/location/LocationManagerServiceUtils.java deleted file mode 100644 index b9d86c84508f..000000000000 --- a/services/core/java/com/android/server/location/LocationManagerServiceUtils.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2020 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.location; - -import android.annotation.NonNull; -import android.location.util.identity.CallerIdentity; -import android.os.IBinder; -import android.os.RemoteException; - -import java.util.NoSuchElementException; - -/** - * Shared utilities for LocationManagerService and GnssManager. - */ -public class LocationManagerServiceUtils { - - /** - * Skeleton class of listener that can be linked to a binder. - */ - public abstract static class LinkedListenerBase implements IBinder.DeathRecipient { - protected final CallerIdentity mCallerIdentity; - - LinkedListenerBase(@NonNull CallerIdentity callerIdentity) { - mCallerIdentity = callerIdentity; - } - - @Override - public String toString() { - return mCallerIdentity.toString(); - } - - public CallerIdentity getCallerIdentity() { - return mCallerIdentity; - } - - /** - * Link listener (i.e. callback) to a binder, so that it will be called upon binder's death. - */ - public boolean linkToListenerDeathNotificationLocked(IBinder binder) { - try { - binder.linkToDeath(this, 0 /* flags */); - return true; - } catch (RemoteException e) { - return false; - } - } - - /** - * Unlink death listener (i.e. callback) from binder. - */ - public void unlinkFromListenerDeathNotificationLocked(IBinder binder) { - try { - binder.unlinkToDeath(this, 0 /* flags */); - } catch (NoSuchElementException e) { - // ignore - } - } - } -} diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java new file mode 100644 index 000000000000..d4f8c7e855b9 --- /dev/null +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -0,0 +1,1951 @@ +/* + * Copyright (C) 2020 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.location; + +import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; +import static android.app.AlarmManager.WINDOW_EXACT; +import static android.location.LocationManager.FUSED_PROVIDER; +import static android.location.LocationManager.GPS_PROVIDER; +import static android.location.LocationManager.KEY_LOCATION_CHANGED; +import static android.location.LocationManager.KEY_PROVIDER_ENABLED; +import static android.location.LocationManager.PASSIVE_PROVIDER; +import static android.os.IPowerManager.LOCATION_MODE_NO_CHANGE; +import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF; +import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY; +import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF; +import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; + +import static com.android.server.location.LocationManagerService.D; +import static com.android.server.location.LocationManagerService.TAG; +import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; +import static com.android.server.location.LocationPermissions.PERMISSION_FINE; +import static com.android.server.location.LocationPermissions.PERMISSION_NONE; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import android.annotation.Nullable; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.location.Criteria; +import android.location.ILocationCallback; +import android.location.ILocationListener; +import android.location.Location; +import android.location.LocationManager; +import android.location.LocationManagerInternal; +import android.location.LocationManagerInternal.ProviderEnabledListener; +import android.location.LocationRequest; +import android.location.util.identity.CallerIdentity; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.IBinder; +import android.os.ICancellationSignal; +import android.os.IRemoteCallback; +import android.os.PowerManager; +import android.os.PowerManager.LocationPowerSaveMode; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.os.WorkSource; +import android.stats.location.LocationStatsEnums; +import android.text.TextUtils; +import android.util.ArraySet; +import android.util.EventLog; +import android.util.IndentingPrintWriter; +import android.util.Log; +import android.util.SparseArray; +import android.util.SparseBooleanArray; +import android.util.TimeUtils; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; +import com.android.internal.util.Preconditions; +import com.android.server.FgThread; +import com.android.server.LocalServices; +import com.android.server.PendingIntentUtils; +import com.android.server.location.LocationPermissions.PermissionLevel; +import com.android.server.location.listeners.ListenerMultiplexer; +import com.android.server.location.listeners.RemovableListenerRegistration; +import com.android.server.location.util.AppForegroundHelper; +import com.android.server.location.util.AppForegroundHelper.AppForegroundListener; +import com.android.server.location.util.AppOpsHelper; +import com.android.server.location.util.Injector; +import com.android.server.location.util.LocationAttributionHelper; +import com.android.server.location.util.LocationPermissionsHelper; +import com.android.server.location.util.LocationPermissionsHelper.LocationPermissionsListener; +import com.android.server.location.util.LocationPowerSaveModeHelper; +import com.android.server.location.util.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener; +import com.android.server.location.util.LocationUsageLogger; +import com.android.server.location.util.ScreenInteractiveHelper; +import com.android.server.location.util.ScreenInteractiveHelper.ScreenInteractiveChangedListener; +import com.android.server.location.util.SettingsHelper; +import com.android.server.location.util.SettingsHelper.GlobalSettingChangedListener; +import com.android.server.location.util.SettingsHelper.UserSettingChangedListener; +import com.android.server.location.util.UserInfoHelper; +import com.android.server.location.util.UserInfoHelper.UserListener; + +import java.io.FileDescriptor; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +class LocationProviderManager extends + ListenerMultiplexer<Object, LocationRequest, LocationProviderManager.LocationTransport, + LocationProviderManager.Registration, ProviderRequest> implements + AbstractLocationProvider.Listener { + + // fastest interval at which clients may receive coarse locations + public static final long FASTEST_COARSE_INTERVAL_MS = 10 * 60 * 1000; + + private static final String WAKELOCK_TAG = "*location*"; + private static final long WAKELOCK_TIMEOUT_MS = 30 * 1000; + + // maximum interval to be considered "high power" request + private static final long MAX_HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000; + + // maximum age of a location before it is no longer considered "current" + private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000; + + // max timeout allowed for getting the current location + private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000; + + // maximum jitter allowed for fastest interval evaluation + private static final int MAX_FASTEST_INTERVAL_JITTER_MS = 100; + + protected interface LocationTransport { + + void deliverOnLocationChanged(Location location, @Nullable Runnable onCompleteCallback) + throws Exception; + } + + protected interface ProviderTransport { + + void deliverOnProviderEnabledChanged(String provider, boolean enabled) throws Exception; + } + + protected static final class LocationListenerTransport implements LocationTransport, + ProviderTransport { + + private final ILocationListener mListener; + + protected LocationListenerTransport(ILocationListener listener) { + mListener = Objects.requireNonNull(listener); + } + + @Override + public void deliverOnLocationChanged(Location location, + @Nullable Runnable onCompleteCallback) + throws RemoteException { + mListener.onLocationChanged(location, + onCompleteCallback == null ? null : new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle data) { + onCompleteCallback.run(); + } + }); + } + + @Override + public void deliverOnProviderEnabledChanged(String provider, boolean enabled) + throws RemoteException { + mListener.onProviderEnabledChanged(provider, enabled); + } + } + + protected static final class LocationPendingIntentTransport implements LocationTransport, + ProviderTransport { + + private final Context mContext; + private final PendingIntent mPendingIntent; + + public LocationPendingIntentTransport(Context context, PendingIntent pendingIntent) { + mContext = context; + mPendingIntent = pendingIntent; + } + + @Override + public void deliverOnLocationChanged(Location location, + @Nullable Runnable onCompleteCallback) + throws PendingIntent.CanceledException { + mPendingIntent.send(mContext, 0, new Intent().putExtra(KEY_LOCATION_CHANGED, location), + onCompleteCallback != null ? (pI, i, rC, rD, rE) -> onCompleteCallback.run() + : null, null, null, + PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); + } + + @Override + public void deliverOnProviderEnabledChanged(String provider, boolean enabled) + throws PendingIntent.CanceledException { + mPendingIntent.send(mContext, 0, new Intent().putExtra(KEY_PROVIDER_ENABLED, enabled), + null, null, null, + PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); + } + } + + protected static final class GetCurrentLocationTransport implements LocationTransport { + + private final ILocationCallback mCallback; + + protected GetCurrentLocationTransport(ILocationCallback callback) { + mCallback = Objects.requireNonNull(callback); + } + + @Override + public void deliverOnLocationChanged(Location location, + @Nullable Runnable onCompleteCallback) + throws RemoteException { + // ILocationCallback doesn't currently support completion callbacks + Preconditions.checkState(onCompleteCallback == null); + mCallback.onLocation(location); + } + } + + protected abstract class Registration extends + RemovableListenerRegistration<LocationRequest, LocationTransport> { + + @PermissionLevel protected final int mPermissionLevel; + private final WorkSource mWorkSource; + + // we cache these values because checking/calculating on the fly is more expensive + private boolean mPermitted; + private boolean mForeground; + @Nullable private LocationRequest mProviderLocationRequest; + private boolean mIsUsingHighPower; + + protected Registration(LocationRequest request, CallerIdentity identity, + LocationTransport transport, @PermissionLevel int permissionLevel) { + super(TAG, Objects.requireNonNull(request), identity, transport); + + Preconditions.checkArgument(permissionLevel > PERMISSION_NONE); + mPermissionLevel = permissionLevel; + + if (request.getWorkSource() != null && !request.getWorkSource().isEmpty()) { + mWorkSource = request.getWorkSource(); + } else { + mWorkSource = identity.addToWorkSource(null); + } + } + + @GuardedBy("mLock") + @Override + protected final void onRemovableListenerRegister() { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + if (D) { + Log.d(TAG, mName + " provider added registration from " + getIdentity() + " -> " + + getRequest()); + } + + // initialization order is important as there are ordering dependencies + mPermitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel, + getIdentity()); + mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid()); + mProviderLocationRequest = calculateProviderLocationRequest(); + mIsUsingHighPower = isUsingHighPower(); + + onProviderListenerRegister(); + } + + @GuardedBy("mLock") + @Override + protected final void onRemovableListenerUnregister() { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + onProviderListenerUnregister(); + + if (D) { + Log.d(TAG, mName + " provider removed registration from " + getIdentity()); + } + } + + /** + * Subclasses may override this instead of {@link #onRemovableListenerRegister()}. + */ + @GuardedBy("mLock") + protected void onProviderListenerRegister() {} + + /** + * Subclasses may override this instead of {@link #onRemovableListenerUnregister()}. + */ + @GuardedBy("mLock") + protected void onProviderListenerUnregister() {} + + @Override + protected final ListenerOperation<LocationTransport> onActive() { + if (!getRequest().getHideFromAppOps()) { + mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey()); + } + onHighPowerUsageChanged(); + return null; + } + + @Override + protected final void onInactive() { + onHighPowerUsageChanged(); + if (!getRequest().getHideFromAppOps()) { + mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey()); + } + } + + @Override + public final LocationRequest getRequest() { + return Objects.requireNonNull(mProviderLocationRequest); + } + + public final boolean isForeground() { + return mForeground; + } + + public final boolean isPermitted() { + return mPermitted; + } + + @Override + protected final LocationProviderManager getOwner() { + return LocationProviderManager.this; + } + + protected final WorkSource getWorkSource() { + return mWorkSource; + } + + @GuardedBy("mLock") + private void onHighPowerUsageChanged() { + boolean isUsingHighPower = isUsingHighPower(); + if (isUsingHighPower != mIsUsingHighPower) { + mIsUsingHighPower = isUsingHighPower; + + if (!getRequest().getHideFromAppOps()) { + if (mIsUsingHighPower) { + mLocationAttributionHelper.reportHighPowerLocationStart( + getIdentity(), getName(), getKey()); + } else { + mLocationAttributionHelper.reportHighPowerLocationStop( + getIdentity(), getName(), getKey()); + } + } + } + } + + @GuardedBy("mLock") + private boolean isUsingHighPower() { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + return isActive() + && getRequest().getInterval() < MAX_HIGH_POWER_INTERVAL_MS + && getProperties().mPowerRequirement == Criteria.POWER_HIGH; + } + + @GuardedBy("mLock") + final boolean onLocationPermissionsChanged(String packageName) { + if (getIdentity().getPackageName().equals(packageName)) { + return onLocationPermissionsChanged(); + } + + return false; + } + + @GuardedBy("mLock") + final boolean onLocationPermissionsChanged(int uid) { + if (getIdentity().getUid() == uid) { + return onLocationPermissionsChanged(); + } + + return false; + } + + @GuardedBy("mLock") + private boolean onLocationPermissionsChanged() { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel, + getIdentity()); + if (permitted != mPermitted) { + if (D) { + Log.v(TAG, mName + " provider package " + getIdentity().getPackageName() + + " permitted = " + permitted); + } + + mPermitted = permitted; + return true; + } + + return false; + } + + @GuardedBy("mLock") + final boolean onForegroundChanged(int uid, boolean foreground) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + if (getIdentity().getUid() == uid && foreground != mForeground) { + if (D) { + Log.v(TAG, mName + " provider uid " + uid + " foreground = " + foreground); + } + + mForeground = foreground; + + // note that onProviderLocationRequestChanged() is always called + return onProviderLocationRequestChanged() + || mLocationPowerSaveModeHelper.getLocationPowerSaveMode() + == LOCATION_MODE_FOREGROUND_ONLY; + } + + return false; + } + + @GuardedBy("mLock") + final boolean onProviderLocationRequestChanged() { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + LocationRequest newRequest = calculateProviderLocationRequest(); + if (!mProviderLocationRequest.equals(newRequest)) { + LocationRequest oldRequest = mProviderLocationRequest; + mProviderLocationRequest = newRequest; + onHighPowerUsageChanged(); + updateService(); + + // if location settings ignored has changed then the active state may have changed + return oldRequest.isLocationSettingsIgnored() + != newRequest.isLocationSettingsIgnored(); + } + + return false; + } + + private LocationRequest calculateProviderLocationRequest() { + LocationRequest newRequest = new LocationRequest(super.getRequest()); + + if (newRequest.isLocationSettingsIgnored()) { + // if we are not currently allowed use location settings ignored, disable it + if (!mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains( + getIdentity().getPackageName()) && !mLocationManagerInternal.isProvider( + null, getIdentity())) { + newRequest.setLocationSettingsIgnored(false); + } + } + + if (!newRequest.isLocationSettingsIgnored() && !isThrottlingExempt()) { + // throttle in the background + if (!mForeground) { + newRequest.setInterval(Math.max(newRequest.getInterval(), + mSettingsHelper.getBackgroundThrottleIntervalMs())); + } + } + + return newRequest; + } + + private boolean isThrottlingExempt() { + if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains( + getIdentity().getPackageName())) { + return true; + } + + return mLocationManagerInternal.isProvider(null, getIdentity()); + } + + @Nullable + abstract ListenerOperation<LocationTransport> acceptLocationChange(Location fineLocation); + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getIdentity()); + + ArraySet<String> flags = new ArraySet<>(2); + if (!isForeground()) { + flags.add("bg"); + } + if (!isPermitted()) { + flags.add("na"); + } + if (!flags.isEmpty()) { + builder.append(" ").append(flags); + } + + if (mPermissionLevel == PERMISSION_COARSE) { + builder.append(" (COARSE)"); + } + + builder.append(" ").append(getRequest()); + return builder.toString(); + } + } + + protected abstract class LocationRegistration extends Registration implements + AlarmManager.OnAlarmListener, ProviderEnabledListener { + + private final PowerManager.WakeLock mWakeLock; + + private volatile ProviderTransport mProviderTransport; + @Nullable private Location mLastLocation = null; + private int mNumLocationsDelivered = 0; + private long mExpirationRealtimeMs = Long.MAX_VALUE; + + protected <TTransport extends LocationTransport & ProviderTransport> LocationRegistration( + LocationRequest request, CallerIdentity identity, TTransport transport, + @PermissionLevel int permissionLevel) { + super(request, identity, transport, permissionLevel); + mProviderTransport = transport; + mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class)) + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); + mWakeLock.setReferenceCounted(true); + mWakeLock.setWorkSource(getWorkSource()); + } + + @Override + protected void onListenerUnregister() { + mProviderTransport = null; + } + + @GuardedBy("mLock") + @Override + protected final void onProviderListenerRegister() { + mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs( + SystemClock.elapsedRealtime()); + + // add alarm for expiration + if (mExpirationRealtimeMs < SystemClock.elapsedRealtime()) { + remove(); + } else if (mExpirationRealtimeMs < Long.MAX_VALUE) { + AlarmManager alarmManager = Objects.requireNonNull( + mContext.getSystemService(AlarmManager.class)); + alarmManager.set(ELAPSED_REALTIME_WAKEUP, mExpirationRealtimeMs, WINDOW_EXACT, + 0, this, FgThread.getHandler(), getWorkSource()); + } + + // start listening for provider enabled/disabled events + addEnabledListener(this); + + onLocationListenerRegister(); + + // if the provider is currently disabled, let the client know immediately + int userId = getIdentity().getUserId(); + if (!isEnabled(userId)) { + onProviderEnabledChanged(mName, userId, false); + } + } + + @GuardedBy("mLock") + @Override + protected final void onProviderListenerUnregister() { + // stop listening for provider enabled/disabled events + removeEnabledListener(this); + + // remove alarm for expiration + if (mExpirationRealtimeMs < Long.MAX_VALUE) { + AlarmManager alarmManager = Objects.requireNonNull( + mContext.getSystemService(AlarmManager.class)); + alarmManager.cancel(this); + } + + onLocationListenerUnregister(); + } + + /** + * Subclasses may override this instead of {@link #onRemovableListenerRegister()}. + */ + @GuardedBy("mLock") + protected void onLocationListenerRegister() {} + + /** + * Subclasses may override this instead of {@link #onRemovableListenerUnregister()}. + */ + @GuardedBy("mLock") + protected void onLocationListenerUnregister() {} + + @Override + public void onAlarm() { + if (D) { + Log.d(TAG, "removing " + getIdentity() + " from " + mName + + " provider due to expiration at " + TimeUtils.formatRealtime( + mExpirationRealtimeMs)); + } + + synchronized (mLock) { + remove(); + } + } + + @GuardedBy("mLock") + @Nullable + @Override + ListenerOperation<LocationTransport> acceptLocationChange(Location fineLocation) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + // check expiration time - alarm is not guaranteed to go off at the right time, + // especially for short intervals + if (SystemClock.elapsedRealtime() >= mExpirationRealtimeMs) { + remove(); + return null; + } + + Location location; + switch (mPermissionLevel) { + case PERMISSION_FINE: + location = fineLocation; + break; + case PERMISSION_COARSE: + location = mLocationFudger.createCoarse(fineLocation); + break; + default: + // shouldn't be possible to have a client added without location permissions + throw new AssertionError(); + } + + if (mLastLocation != null) { + // check fastest interval + long deltaMs = NANOSECONDS.toMillis( + location.getElapsedRealtimeNanos() + - mLastLocation.getElapsedRealtimeNanos()); + if (deltaMs < getRequest().getFastestInterval() - MAX_FASTEST_INTERVAL_JITTER_MS) { + return null; + } + + // check smallest displacement + double smallestDisplacement = getRequest().getSmallestDisplacement(); + if (smallestDisplacement > 0.0 && location.distanceTo(mLastLocation) + <= smallestDisplacement) { + return null; + } + } + + // note app ops + if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel), + getIdentity())) { + if (D) { + Log.w(TAG, "noteOp denied for " + getIdentity()); + } + return null; + } + + // update last location + mLastLocation = location; + + return new ListenerOperation<LocationTransport>() { + @Override + public void onPreExecute() { + // don't acquire a wakelock for mock locations to prevent abuse + if (!location.isFromMockProvider()) { + mWakeLock.acquire(WAKELOCK_TIMEOUT_MS); + } + } + + @Override + public void operate(LocationTransport listener) throws Exception { + // if delivering to the same process, make a copy of the location first (since + // location is mutable) + Location deliveryLocation; + if (getIdentity().getPid() == Process.myPid()) { + deliveryLocation = new Location(location); + } else { + deliveryLocation = location; + } + + listener.deliverOnLocationChanged(deliveryLocation, + location.isFromMockProvider() ? null : mWakeLock::release); + } + + @Override + public void onPostExecute(boolean success) { + if (!success && !location.isFromMockProvider()) { + mWakeLock.release(); + } + + if (success) { + // check num updates - if successful then this function will always be run + // from the same thread, and no additional synchronization is necessary + boolean remove = ++mNumLocationsDelivered >= getRequest().getNumUpdates(); + if (remove) { + if (D) { + Log.d(TAG, "removing " + getIdentity() + " from " + mName + + " provider due to number of updates"); + } + + synchronized (mLock) { + remove(); + } + } + } + } + }; + } + + @Override + public void onProviderEnabledChanged(String provider, int userId, boolean enabled) { + Preconditions.checkState(mName.equals(provider)); + + if (userId != getIdentity().getUserId()) { + return; + } + + // we choose not to hold a wakelock for provider enabled changed events + executeSafely(getExecutor(), () -> mProviderTransport, + listener -> listener.deliverOnProviderEnabledChanged(mName, enabled)); + } + } + + protected final class LocationListenerRegistration extends LocationRegistration implements + IBinder.DeathRecipient { + + protected LocationListenerRegistration(LocationRequest request, CallerIdentity identity, + LocationListenerTransport transport, @PermissionLevel int permissionLevel) { + super(request, identity, transport, permissionLevel); + } + + @GuardedBy("mLock") + @Override + protected void onLocationListenerRegister() { + try { + ((IBinder) getKey()).linkToDeath(this, 0); + } catch (RemoteException e) { + remove(); + } + } + + @GuardedBy("mLock") + @Override + protected void onLocationListenerUnregister() { + ((IBinder) getKey()).unlinkToDeath(this, 0); + } + + @Override + public void binderDied() { + try { + if (D) { + Log.d(TAG, mName + " provider client died: " + getIdentity()); + } + + synchronized (mLock) { + remove(); + } + } catch (RuntimeException e) { + // the caller may swallow runtime exceptions, so we rethrow as assertion errors to + // ensure the crash is seen + throw new AssertionError(e); + } + } + } + + protected final class LocationPendingIntentRegistration extends LocationRegistration implements + PendingIntent.CancelListener { + + protected LocationPendingIntentRegistration(LocationRequest request, + CallerIdentity identity, LocationPendingIntentTransport transport, + @PermissionLevel int permissionLevel) { + super(request, identity, transport, permissionLevel); + } + + @GuardedBy("mLock") + @Override + protected void onLocationListenerRegister() { + ((PendingIntent) getKey()).registerCancelListener(this); + } + + @GuardedBy("mLock") + @Override + protected void onLocationListenerUnregister() { + ((PendingIntent) getKey()).unregisterCancelListener(this); + } + + @Override + public void onCancelled(PendingIntent intent) { + synchronized (mLock) { + remove(); + } + } + } + + protected final class GetCurrentLocationListenerRegistration extends Registration implements + IBinder.DeathRecipient, ProviderEnabledListener, AlarmManager.OnAlarmListener { + + private volatile LocationTransport mTransport; + + private long mExpirationRealtimeMs = Long.MAX_VALUE; + + protected GetCurrentLocationListenerRegistration(LocationRequest request, + CallerIdentity identity, LocationTransport transport, int permissionLevel) { + super(request, identity, transport, permissionLevel); + mTransport = transport; + } + + @GuardedBy("mLock") + void deliverLocation(@Nullable Location location) { + executeSafely(getExecutor(), () -> mTransport, acceptLocationChange(location)); + } + + @Override + protected void onListenerUnregister() { + mTransport = null; + } + + @GuardedBy("mLock") + @Override + protected void onProviderListenerRegister() { + mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs( + SystemClock.elapsedRealtime()); + + // add alarm for expiration + if (mExpirationRealtimeMs < Long.MAX_VALUE) { + AlarmManager alarmManager = Objects.requireNonNull( + mContext.getSystemService(AlarmManager.class)); + alarmManager.set(ELAPSED_REALTIME_WAKEUP, mExpirationRealtimeMs, WINDOW_EXACT, + 0, this, FgThread.getHandler(), getWorkSource()); + } + + try { + ((IBinder) getKey()).linkToDeath(this, 0); + } catch (RemoteException e) { + remove(); + } + + // start listening for provider enabled/disabled events + addEnabledListener(this); + + // if the provider is currently disabled fail immediately + int userId = getIdentity().getUserId(); + if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) { + deliverLocation(null); + } + } + + @GuardedBy("mLock") + @Override + protected void onProviderListenerUnregister() { + // stop listening for provider enabled/disabled events + removeEnabledListener(this); + + // remove alarm for expiration + if (mExpirationRealtimeMs < Long.MAX_VALUE) { + AlarmManager alarmManager = Objects.requireNonNull( + mContext.getSystemService(AlarmManager.class)); + alarmManager.cancel(this); + } + + ((IBinder) getKey()).unlinkToDeath(this, 0); + } + + @Override + public void onAlarm() { + if (D) { + Log.d(TAG, "removing " + getIdentity() + " from " + mName + + " provider due to expiration at " + TimeUtils.formatRealtime( + mExpirationRealtimeMs)); + } + + synchronized (mLock) { + deliverLocation(null); + } + } + + @GuardedBy("mLock") + @Nullable + @Override + ListenerOperation<LocationTransport> acceptLocationChange(@Nullable Location fineLocation) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + // lastly - note app ops + Location location; + if (fineLocation == null) { + location = null; + } else if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel), + getIdentity())) { + if (D) { + Log.w(TAG, "noteOp denied for " + getIdentity()); + } + location = null; + } else { + switch (mPermissionLevel) { + case PERMISSION_FINE: + location = fineLocation; + break; + case PERMISSION_COARSE: + location = mLocationFudger.createCoarse(fineLocation); + break; + default: + // shouldn't be possible to have a client added without location permissions + throw new AssertionError(); + } + } + + return listener -> { + // if delivering to the same process, make a copy of the location first (since + // location is mutable) + Location deliveryLocation = location; + if (getIdentity().getPid() == Process.myPid() && location != null) { + deliveryLocation = new Location(location); + } + + // we currently don't hold a wakelock for getCurrentLocation deliveries + listener.deliverOnLocationChanged(deliveryLocation, null); + + synchronized (mLock) { + remove(); + } + }; + } + + @Override + public void onProviderEnabledChanged(String provider, int userId, boolean enabled) { + Preconditions.checkState(mName.equals(provider)); + + if (userId != getIdentity().getUserId()) { + return; + } + + // if the provider is disabled we give up on current location immediately + if (!getRequest().isLocationSettingsIgnored() && !enabled) { + synchronized (mLock) { + deliverLocation(null); + } + } + } + + @Override + public void binderDied() { + try { + if (D) { + Log.d(TAG, mName + " provider client died: " + getIdentity()); + } + + synchronized (mLock) { + remove(); + } + } catch (RuntimeException e) { + // the caller may swallow runtime exceptions, so we rethrow as assertion errors to + // ensure the crash is seen + throw new AssertionError(e); + } + } + } + + protected final Object mLock = new Object(); + + protected final String mName; + @Nullable private final PassiveLocationProviderManager mPassiveManager; + + protected final Context mContext; + + @GuardedBy("mLock") + private boolean mStarted; + + // maps of user id to value + @GuardedBy("mLock") + private final SparseBooleanArray mEnabled; // null or not present means unknown + @GuardedBy("mLock") + private final SparseArray<LastLocation> mLastLocations; + + @GuardedBy("mLock") + private final ArrayList<ProviderEnabledListener> mEnabledListeners; + + protected final LocationManagerInternal mLocationManagerInternal; + protected final SettingsHelper mSettingsHelper; + protected final UserInfoHelper mUserInfoHelper; + protected final AppOpsHelper mAppOpsHelper; + protected final LocationPermissionsHelper mLocationPermissionsHelper; + protected final AppForegroundHelper mAppForegroundHelper; + protected final LocationPowerSaveModeHelper mLocationPowerSaveModeHelper; + protected final ScreenInteractiveHelper mScreenInteractiveHelper; + protected final LocationAttributionHelper mLocationAttributionHelper; + protected final LocationUsageLogger mLocationUsageLogger; + protected final LocationFudger mLocationFudger; + protected final LocationRequestStatistics mLocationRequestStatistics; + + private final UserListener mUserChangedListener = this::onUserChanged; + private final UserSettingChangedListener mLocationEnabledChangedListener = + this::onLocationEnabledChanged; + private final GlobalSettingChangedListener mBackgroundThrottlePackageWhitelistChangedListener = + this::onBackgroundThrottlePackageWhitelistChanged; + private final UserSettingChangedListener mLocationPackageBlacklistChangedListener = + this::onLocationPackageBlacklistChanged; + private final LocationPermissionsListener mLocationPermissionsListener = + new LocationPermissionsListener() { + @Override + public void onLocationPermissionsChanged(String packageName) { + LocationProviderManager.this.onLocationPermissionsChanged(packageName); + } + + @Override + public void onLocationPermissionsChanged(int uid) { + LocationProviderManager.this.onLocationPermissionsChanged(uid); + } + }; + private final AppForegroundListener mAppForegroundChangedListener = + this::onAppForegroundChanged; + private final GlobalSettingChangedListener mBackgroundThrottleIntervalChangedListener = + this::onBackgroundThrottleIntervalChanged; + private final GlobalSettingChangedListener mIgnoreSettingsPackageWhitelistChangedListener = + this::onIgnoreSettingsWhitelistChanged; + private final LocationPowerSaveModeChangedListener mLocationPowerSaveModeChangedListener = + this::onLocationPowerSaveModeChanged; + private final ScreenInteractiveChangedListener mScreenInteractiveChangedListener = + this::onScreenInteractiveChanged; + + // acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary + protected final MockableLocationProvider mProvider; + + LocationProviderManager(Context context, Injector injector, String name, + @Nullable PassiveLocationProviderManager passiveManager) { + mContext = context; + mName = Objects.requireNonNull(name); + mPassiveManager = passiveManager; + mStarted = false; + mEnabled = new SparseBooleanArray(2); + mLastLocations = new SparseArray<>(2); + + mEnabledListeners = new ArrayList<>(); + + mLocationManagerInternal = Objects.requireNonNull( + LocalServices.getService(LocationManagerInternal.class)); + mSettingsHelper = injector.getSettingsHelper(); + mUserInfoHelper = injector.getUserInfoHelper(); + mAppOpsHelper = injector.getAppOpsHelper(); + mLocationPermissionsHelper = injector.getLocationPermissionsHelper(); + mAppForegroundHelper = injector.getAppForegroundHelper(); + mLocationPowerSaveModeHelper = injector.getLocationPowerSaveModeHelper(); + mScreenInteractiveHelper = injector.getScreenInteractiveHelper(); + mLocationAttributionHelper = injector.getLocationAttributionHelper(); + mLocationUsageLogger = injector.getLocationUsageLogger(); + mLocationRequestStatistics = injector.getLocationRequestStatistics(); + mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM()); + + // initialize last since this lets our reference escape + mProvider = new MockableLocationProvider(mLock, this); + } + + public void startManager() { + synchronized (mLock) { + mStarted = true; + + mUserInfoHelper.addListener(mUserChangedListener); + mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener); + + // initialize enabled state + onUserStarted(UserHandle.USER_ALL); + } + } + + public void stopManager() { + synchronized (mLock) { + mUserInfoHelper.removeListener(mUserChangedListener); + mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener); + + // notify and remove all listeners + onUserStopped(UserHandle.USER_ALL); + removeRegistrationIf(key -> true); + mEnabledListeners.clear(); + + mStarted = false; + } + } + + public String getName() { + return mName; + } + + @Nullable + public CallerIdentity getIdentity() { + return mProvider.getState().identity; + } + + @Nullable + public ProviderProperties getProperties() { + return mProvider.getState().properties; + } + + public boolean hasProvider() { + return mProvider.getProvider() != null; + } + + public boolean isEnabled(int userId) { + if (userId == UserHandle.USER_NULL) { + return false; + } + + Preconditions.checkArgument(userId >= 0); + + synchronized (mLock) { + int index = mEnabled.indexOfKey(userId); + if (index < 0) { + // this generally shouldn't occur, but might be possible due to race conditions + // on when we are notified of new users + Log.w(TAG, mName + " provider saw user " + userId + " unexpectedly"); + onEnabledChanged(userId); + index = mEnabled.indexOfKey(userId); + } + + return mEnabled.valueAt(index); + } + } + + public void addEnabledListener(ProviderEnabledListener listener) { + synchronized (mLock) { + Preconditions.checkState(mStarted); + mEnabledListeners.add(listener); + } + } + + public void removeEnabledListener(ProviderEnabledListener listener) { + synchronized (mLock) { + Preconditions.checkState(mStarted); + mEnabledListeners.remove(listener); + } + } + + public void setRealProvider(AbstractLocationProvider provider) { + synchronized (mLock) { + Preconditions.checkState(mStarted); + mProvider.setRealProvider(provider); + } + } + + public void setMockProvider(@Nullable MockProvider provider) { + synchronized (mLock) { + Preconditions.checkState(mStarted); + mProvider.setMockProvider(provider); + + // when removing a mock provider, also clear any mock last locations and reset the + // location fudger. the mock provider could have been used to infer the current + // location fudger offsets. + if (provider == null) { + final int lastLocationSize = mLastLocations.size(); + for (int i = 0; i < lastLocationSize; i++) { + mLastLocations.valueAt(i).clearMock(); + } + + mLocationFudger.resetOffsets(); + } + } + } + + public void setMockProviderAllowed(boolean enabled) { + synchronized (mLock) { + if (!mProvider.isMock()) { + throw new IllegalArgumentException(mName + " provider is not a test provider"); + } + + mProvider.setMockProviderAllowed(enabled); + } + } + + public void setMockProviderLocation(Location location) { + synchronized (mLock) { + if (!mProvider.isMock()) { + throw new IllegalArgumentException(mName + " provider is not a test provider"); + } + + String locationProvider = location.getProvider(); + if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) { + // The location has an explicit provider that is different from the mock + // provider name. The caller may be trying to fool us via b/33091107. + EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(), + mName + "!=" + locationProvider); + } + + mProvider.setMockProviderLocation(location); + } + } + + public List<LocationRequest> getMockProviderRequests() { + synchronized (mLock) { + if (!mProvider.isMock()) { + throw new IllegalArgumentException(mName + " provider is not a test provider"); + } + + return mProvider.getCurrentRequest().locationRequests; + } + } + + @Nullable + public Location getLastLocation(LocationRequest request, CallerIdentity identity, + @PermissionLevel int permissionLevel) { + Preconditions.checkArgument(mName.equals(request.getProvider())); + + if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), + identity.getPackageName())) { + return null; + } + if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) { + return null; + } + if (!request.isLocationSettingsIgnored() && !isEnabled(identity.getUserId())) { + return null; + } + + Location location = getLastLocation(identity.getUserId(), permissionLevel, + request.isLocationSettingsIgnored()); + + // we don't note op here because we don't know what the client intends to do with the + // location, the client is responsible for noting if necessary + + if (identity.getPid() == Process.myPid() && location != null) { + // if delivering to the same process, make a copy of the location first (since + // location is mutable) + return new Location(location); + } else { + return location; + } + } + + @Nullable + private Location getLastLocation(int userId, @PermissionLevel int permissionLevel, + boolean ignoreLocationSettings) { + synchronized (mLock) { + LastLocation lastLocation = mLastLocations.get(userId); + if (lastLocation == null) { + return null; + } + return lastLocation.get(permissionLevel, ignoreLocationSettings); + } + } + + public void injectLastLocation(Location location, int userId) { + synchronized (mLock) { + if (getLastLocation(userId, PERMISSION_FINE, false) == null) { + setLastLocation(location, userId); + } + } + } + + private void setLastLocation(Location location, int userId) { + if (userId == UserHandle.USER_ALL) { + final int[] runningUserIds = mUserInfoHelper.getRunningUserIds(); + for (int i = 0; i < runningUserIds.length; i++) { + setLastLocation(location, runningUserIds[i]); + } + return; + } + + Preconditions.checkArgument(userId >= 0); + + synchronized (mLock) { + LastLocation lastLocation = mLastLocations.get(userId); + if (lastLocation == null) { + lastLocation = new LastLocation(); + mLastLocations.put(userId, lastLocation); + } + + Location coarseLocation = mLocationFudger.createCoarse(location); + if (isEnabled(userId)) { + lastLocation.set(location, coarseLocation); + } + lastLocation.setBypass(location, coarseLocation); + } + } + + public void getCurrentLocation(LocationRequest request, CallerIdentity identity, + int permissionLevel, ICancellationSignal cancellationTransport, + ILocationCallback callback) { + Preconditions.checkArgument(mName.equals(request.getProvider())); + + if (request.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) { + request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS); + } + + GetCurrentLocationListenerRegistration registration = + new GetCurrentLocationListenerRegistration( + request, + identity, + new GetCurrentLocationTransport(callback), + permissionLevel); + + synchronized (mLock) { + Location lastLocation = getLastLocation(request, identity, permissionLevel); + if (lastLocation != null) { + long locationAgeMs = NANOSECONDS.toMillis( + SystemClock.elapsedRealtimeNanos() + - lastLocation.getElapsedRealtimeNanos()); + if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) { + registration.deliverLocation(lastLocation); + return; + } + + if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid()) + && locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) { + registration.deliverLocation(null); + return; + } + } + + // if last location isn't good enough then we add a location request + addRegistration(callback.asBinder(), registration); + CancellationSignal cancellationSignal = CancellationSignal.fromTransport( + cancellationTransport); + if (cancellationSignal != null) { + cancellationSignal.setOnCancelListener( + () -> { + synchronized (mLock) { + removeRegistration(callback.asBinder(), registration); + } + }); + } + } + } + + public void sendExtraCommand(int uid, int pid, String command, Bundle extras) { + mProvider.sendExtraCommand(uid, pid, command, extras); + } + + public void registerLocationRequest(LocationRequest request, CallerIdentity identity, + @PermissionLevel int permissionLevel, ILocationListener listener) { + Preconditions.checkArgument(mName.equals(request.getProvider())); + + synchronized (mLock) { + addRegistration( + listener.asBinder(), + new LocationListenerRegistration( + request, + identity, + new LocationListenerTransport(listener), + permissionLevel)); + } + } + + public void registerLocationRequest(LocationRequest request, CallerIdentity identity, + @PermissionLevel int permissionLevel, PendingIntent pendingIntent) { + Preconditions.checkArgument(mName.equals(request.getProvider())); + + synchronized (mLock) { + addRegistration( + pendingIntent, + new LocationPendingIntentRegistration( + request, + identity, + new LocationPendingIntentTransport(mContext, pendingIntent), + permissionLevel)); + } + } + + public void unregisterLocationRequest(ILocationListener listener) { + synchronized (mLock) { + removeRegistration(listener.asBinder()); + } + } + + public void unregisterLocationRequest(PendingIntent pendingIntent) { + synchronized (mLock) { + removeRegistration(pendingIntent); + } + } + + @GuardedBy("mLock") + @Override + protected void onRegister() { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + mSettingsHelper.addOnBackgroundThrottleIntervalChangedListener( + mBackgroundThrottleIntervalChangedListener); + mSettingsHelper.addOnBackgroundThrottlePackageWhitelistChangedListener( + mBackgroundThrottlePackageWhitelistChangedListener); + mSettingsHelper.addOnLocationPackageBlacklistChangedListener( + mLocationPackageBlacklistChangedListener); + mSettingsHelper.addOnIgnoreSettingsPackageWhitelistChangedListener( + mIgnoreSettingsPackageWhitelistChangedListener); + mLocationPermissionsHelper.addListener(mLocationPermissionsListener); + mAppForegroundHelper.addListener(mAppForegroundChangedListener); + mLocationPowerSaveModeHelper.addListener(mLocationPowerSaveModeChangedListener); + mScreenInteractiveHelper.addListener(mScreenInteractiveChangedListener); + } + + @GuardedBy("mLock") + @Override + protected void onUnregister() { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + mSettingsHelper.removeOnBackgroundThrottleIntervalChangedListener( + mBackgroundThrottleIntervalChangedListener); + mSettingsHelper.removeOnBackgroundThrottlePackageWhitelistChangedListener( + mBackgroundThrottlePackageWhitelistChangedListener); + mSettingsHelper.removeOnLocationPackageBlacklistChangedListener( + mLocationPackageBlacklistChangedListener); + mSettingsHelper.removeOnIgnoreSettingsPackageWhitelistChangedListener( + mIgnoreSettingsPackageWhitelistChangedListener); + mLocationPermissionsHelper.removeListener(mLocationPermissionsListener); + mAppForegroundHelper.removeListener(mAppForegroundChangedListener); + mLocationPowerSaveModeHelper.removeListener(mLocationPowerSaveModeChangedListener); + mScreenInteractiveHelper.removeListener(mScreenInteractiveChangedListener); + } + + @GuardedBy("mLock") + @Override + protected void onRegistrationAdded(Object key, Registration registration) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + mLocationUsageLogger.logLocationApiUsage( + LocationStatsEnums.USAGE_STARTED, + LocationStatsEnums.API_REQUEST_LOCATION_UPDATES, + registration.getIdentity().getPackageName(), + registration.getRequest(), + key instanceof PendingIntent, + key instanceof IBinder, + /* geofence= */ null, + registration.isForeground()); + + mLocationRequestStatistics.startRequesting( + registration.getIdentity().getPackageName(), + registration.getIdentity().getAttributionTag(), + mName, + registration.getRequest().getInterval(), + registration.isForeground()); + } + + @GuardedBy("mLock") + @Override + protected void onRegistrationRemoved(Object key, Registration registration) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + mLocationRequestStatistics.stopRequesting( + registration.getIdentity().getPackageName(), + registration.getIdentity().getAttributionTag(), + mName); + + mLocationUsageLogger.logLocationApiUsage( + LocationStatsEnums.USAGE_ENDED, + LocationStatsEnums.API_REQUEST_LOCATION_UPDATES, + registration.getIdentity().getPackageName(), + registration.getRequest(), + key instanceof PendingIntent, + key instanceof IBinder, + /* geofence= */ null, + registration.isForeground()); + } + + @GuardedBy("mLock") + @Override + protected boolean registerWithService(ProviderRequest mergedRequest) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + mProvider.setRequest(mergedRequest); + return true; + } + + @GuardedBy("mLock") + @Override + protected boolean reregisterWithService(ProviderRequest oldRequest, + ProviderRequest newRequest) { + return registerWithService(newRequest); + } + + @GuardedBy("mLock") + @Override + protected void unregisterWithService() { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + mProvider.setRequest(ProviderRequest.EMPTY_REQUEST); + } + + @GuardedBy("mLock") + @Override + protected boolean isActive(Registration registration) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + CallerIdentity identity = registration.getIdentity(); + + if (!registration.isPermitted()) { + return false; + } + + if (!registration.getRequest().isLocationSettingsIgnored()) { + if (!isEnabled(identity.getUserId())) { + return false; + } + + switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) { + case LOCATION_MODE_FOREGROUND_ONLY: + if (!registration.isForeground()) { + return false; + } + break; + case LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF: + if (!GPS_PROVIDER.equals(mName)) { + break; + } + // fall through + case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF: + // fall through + case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF: + if (!mScreenInteractiveHelper.isInteractive()) { + return false; + } + break; + case LOCATION_MODE_NO_CHANGE: + // fall through + default: + break; + } + } + + return !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), + identity.getPackageName()); + } + + @GuardedBy("mLock") + @Override + protected ProviderRequest mergeRequests(Collection<Registration> registrations) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + ProviderRequest.Builder providerRequest = new ProviderRequest.Builder(); + // initialize the low power mode to true and set to false if any of the records requires + providerRequest.setLowPowerMode(true); + + ArrayList<Registration> providerRegistrations = new ArrayList<>(registrations.size()); + for (Registration registration : registrations) { + LocationRequest locationRequest = registration.getRequest(); + + if (locationRequest.isLocationSettingsIgnored()) { + providerRequest.setLocationSettingsIgnored(true); + } + if (!locationRequest.isLowPowerMode()) { + providerRequest.setLowPowerMode(false); + } + + providerRequest.setInterval( + Math.min(locationRequest.getInterval(), providerRequest.getInterval())); + providerRegistrations.add(registration); + } + + // collect contributing location requests + ArrayList<LocationRequest> providerRequests = new ArrayList<>(providerRegistrations.size()); + final int registrationsSize = providerRegistrations.size(); + for (int i = 0; i < registrationsSize; i++) { + providerRequests.add(providerRegistrations.get(i).getRequest()); + } + + providerRequest.setLocationRequests(providerRequests); + + // calculate who to blame for power in a somewhat arbitrary fashion. we pick a threshold + // interval slightly higher that the minimum interval, and spread the blame across all + // contributing registrations under that threshold. + long thresholdIntervalMs = (providerRequest.getInterval() + 1000) * 3 / 2; + if (thresholdIntervalMs < 0) { + // handle overflow + thresholdIntervalMs = Long.MAX_VALUE; + } + for (int i = 0; i < registrationsSize; i++) { + LocationRequest request = providerRegistrations.get(i).getRequest(); + if (request.getInterval() <= thresholdIntervalMs) { + providerRequest.getWorkSource().add(providerRegistrations.get(i).getWorkSource()); + } + } + + return providerRequest.build(); + } + + private void onUserChanged(int userId, int change) { + synchronized (mLock) { + switch (change) { + case UserListener.CURRENT_USER_CHANGED: + onEnabledChanged(userId); + break; + case UserListener.USER_STARTED: + onUserStarted(userId); + break; + case UserListener.USER_STOPPED: + onUserStopped(userId); + break; + } + } + } + + private void onLocationEnabledChanged(int userId) { + synchronized (mLock) { + onEnabledChanged(userId); + } + } + + private void onScreenInteractiveChanged(boolean screenInteractive) { + synchronized (mLock) { + switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) { + case LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF: + if (!GPS_PROVIDER.equals(mName)) { + break; + } + // fall through + case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF: + // fall through + case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF: + updateService(); + break; + default: + break; + } + } + } + + private void onBackgroundThrottlePackageWhitelistChanged() { + synchronized (mLock) { + updateRegistrations(Registration::onProviderLocationRequestChanged); + } + } + + private void onBackgroundThrottleIntervalChanged() { + synchronized (mLock) { + updateRegistrations(Registration::onProviderLocationRequestChanged); + } + } + + private void onLocationPowerSaveModeChanged(@LocationPowerSaveMode int locationPowerSaveMode) { + synchronized (mLock) { + // this is rare, just assume everything has changed to keep it simple + updateRegistrations(registration -> true); + } + } + + private void onAppForegroundChanged(int uid, boolean foreground) { + synchronized (mLock) { + updateRegistrations(registration -> registration.onForegroundChanged(uid, foreground)); + } + } + + private void onIgnoreSettingsWhitelistChanged() { + synchronized (mLock) { + updateRegistrations(Registration::onProviderLocationRequestChanged); + } + } + + private void onLocationPackageBlacklistChanged(int userId) { + synchronized (mLock) { + updateRegistrations(registration -> registration.getIdentity().getUserId() == userId); + } + } + + private void onLocationPermissionsChanged(String packageName) { + synchronized (mLock) { + updateRegistrations( + registration -> registration.onLocationPermissionsChanged(packageName)); + } + } + + private void onLocationPermissionsChanged(int uid) { + synchronized (mLock) { + updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid)); + } + } + + @GuardedBy("mLock") + @Override + public void onStateChanged( + AbstractLocationProvider.State oldState, AbstractLocationProvider.State newState) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + if (oldState.allowed != newState.allowed) { + onEnabledChanged(UserHandle.USER_ALL); + } + } + + @GuardedBy("mLock") + @Override + public void onReportLocation(Location location) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + // don't validate mock locations + if (!location.isFromMockProvider()) { + if (location.getLatitude() == 0 && location.getLongitude() == 0) { + Log.w(TAG, "blocking 0,0 location from " + mName + " provider"); + return; + } + } + + if (!location.isComplete()) { + Log.w(TAG, "blocking incomplete location from " + mName + " provider"); + return; + } + + // update last location + setLastLocation(location, UserHandle.USER_ALL); + + // notify passive provider + if (mPassiveManager != null) { + mPassiveManager.updateLocation(location); + } + + // attempt listener delivery + deliverToListeners(registration -> { + return registration.acceptLocationChange(location); + }); + } + + @GuardedBy("mLock") + @Override + public void onReportLocation(List<Location> locations) { + if (!GPS_PROVIDER.equals(mName)) { + return; + } + + mLocationManagerInternal.reportGnssBatchLocations(locations); + } + + @GuardedBy("mLock") + private void onUserStarted(int userId) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + if (userId == UserHandle.USER_NULL) { + return; + } + + if (userId == UserHandle.USER_ALL) { + // clear the user's prior enabled state to prevent broadcast of enabled state change + mEnabled.clear(); + onEnabledChanged(UserHandle.USER_ALL); + } else { + Preconditions.checkArgument(userId >= 0); + + // clear the user's prior enabled state to prevent broadcast of enabled state change + mEnabled.delete(userId); + onEnabledChanged(userId); + } + } + + @GuardedBy("mLock") + private void onUserStopped(int userId) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + if (userId == UserHandle.USER_NULL) { + return; + } + + if (userId == UserHandle.USER_ALL) { + onEnabledChanged(UserHandle.USER_ALL); + mEnabled.clear(); + mLastLocations.clear(); + } else { + Preconditions.checkArgument(userId >= 0); + + onEnabledChanged(userId); + mEnabled.delete(userId); + mLastLocations.remove(userId); + } + } + + @GuardedBy("mLock") + private void onEnabledChanged(int userId) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + + if (userId == UserHandle.USER_NULL) { + // used during initialization - ignore since many lower level operations (checking + // settings for instance) do not support the null user + return; + } else if (userId == UserHandle.USER_ALL) { + final int[] runningUserIds = mUserInfoHelper.getRunningUserIds(); + for (int i = 0; i < runningUserIds.length; i++) { + onEnabledChanged(runningUserIds[i]); + } + return; + } + + Preconditions.checkArgument(userId >= 0); + + boolean enabled = mStarted + && mProvider.getState().allowed + && mUserInfoHelper.isCurrentUserId(userId) + && mSettingsHelper.isLocationEnabled(userId); + + int index = mEnabled.indexOfKey(userId); + Boolean wasEnabled = index < 0 ? null : mEnabled.valueAt(index); + if (wasEnabled != null && wasEnabled == enabled) { + return; + } + + mEnabled.put(userId, enabled); + + if (D) { + Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled); + } + + // clear last locations if we become disabled + if (!enabled) { + LastLocation lastLocation = mLastLocations.get(userId); + if (lastLocation != null) { + lastLocation.clearLocations(); + } + } + + // do not send change notifications if we just saw this user for the first time + if (wasEnabled != null) { + // fused and passive provider never get public updates for legacy reasons + if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) { + Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION) + .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName) + .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, enabled) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); + } + + // send updates to internal listeners - since we expect listener changes to be more + // frequent than enabled changes, we use copy-on-read instead of copy-on-write + if (!mEnabledListeners.isEmpty()) { + ProviderEnabledListener[] listeners = mEnabledListeners.toArray( + new ProviderEnabledListener[0]); + FgThread.getHandler().post(() -> { + for (int i = 0; i < listeners.length; i++) { + listeners[i].onProviderEnabledChanged(mName, userId, enabled); + } + }); + } + } + + // update active state of affected registrations + updateRegistrations(registration -> registration.getIdentity().getUserId() == userId); + } + + public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { + synchronized (mLock) { + ipw.print(mName + " provider"); + if (mProvider.isMock()) { + ipw.print(" [mock]"); + } + ipw.println(":"); + ipw.increaseIndent(); + + super.dump(fd, ipw, args); + + int[] userIds = mUserInfoHelper.getRunningUserIds(); + for (int userId : userIds) { + if (userIds.length != 1) { + ipw.println("user " + userId + ":"); + ipw.increaseIndent(); + } + ipw.println("last location=" + getLastLocation(userId, PERMISSION_FINE, false)); + ipw.println("enabled=" + isEnabled(userId)); + if (userIds.length != 1) { + ipw.decreaseIndent(); + } + } + } + + mProvider.dump(fd, ipw, args); + + ipw.decreaseIndent(); + } + + private static class LastLocation { + + @Nullable private Location mFineLocation; + @Nullable private Location mCoarseLocation; + @Nullable private Location mFineBypassLocation; + @Nullable private Location mCoarseBypassLocation; + + public void clearMock() { + if (mFineLocation != null && mFineLocation.isFromMockProvider()) { + mFineLocation = null; + mCoarseLocation = null; + } + if (mFineBypassLocation != null && mFineBypassLocation.isFromMockProvider()) { + mFineBypassLocation = null; + mCoarseBypassLocation = null; + } + } + + public void clearLocations() { + mFineLocation = null; + mCoarseLocation = null; + } + + @Nullable + public Location get(@PermissionLevel int permissionLevel, boolean ignoreLocationSettings) { + switch (permissionLevel) { + case PERMISSION_FINE: + if (ignoreLocationSettings) { + return mFineBypassLocation; + } else { + return mFineLocation; + } + case PERMISSION_COARSE: + if (ignoreLocationSettings) { + return mCoarseBypassLocation; + } else { + return mCoarseLocation; + } + default: + // shouldn't be possible to have a client added without location permissions + throw new AssertionError(); + } + } + + public void set(Location location, Location coarseLocation) { + mFineLocation = location; + mCoarseLocation = calculateNextCoarse(mCoarseLocation, coarseLocation); + } + + public void setBypass(Location location, Location coarseLocation) { + mFineBypassLocation = location; + mCoarseBypassLocation = calculateNextCoarse(mCoarseBypassLocation, coarseLocation); + } + + private Location calculateNextCoarse(@Nullable Location oldCoarse, Location newCoarse) { + if (oldCoarse == null) { + return newCoarse; + } + // update last coarse interval only if enough time has passed + long timeDeltaMs = NANOSECONDS.toMillis(newCoarse.getElapsedRealtimeNanos()) + - NANOSECONDS.toMillis(oldCoarse.getElapsedRealtimeNanos()); + if (timeDeltaMs > FASTEST_COARSE_INTERVAL_MS) { + return newCoarse; + } else { + return oldCoarse; + } + } + } +} diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java index c5bd5e6fed8c..fc88f147ea9d 100644 --- a/services/core/java/com/android/server/location/MockProvider.java +++ b/services/core/java/com/android/server/location/MockProvider.java @@ -40,9 +40,8 @@ public class MockProvider extends AbstractLocationProvider { public MockProvider(ProviderProperties properties, CallerIdentity identity) { // using a direct executor is ok because this class has no locks that could deadlock - super(DIRECT_EXECUTOR); + super(DIRECT_EXECUTOR, identity); setProperties(properties); - setIdentity(identity); } /** Sets the allowed state of this mock provider. */ diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java index 54af1c84d36b..d8d435aa4ac0 100644 --- a/services/core/java/com/android/server/location/MockableLocationProvider.java +++ b/services/core/java/com/android/server/location/MockableLocationProvider.java @@ -241,7 +241,6 @@ public class MockableLocationProvider extends AbstractLocationProvider { if (getState().properties != null) { pw.println("properties=" + getState().properties); } - pw.println("request=" + mRequest); } if (provider != null) { diff --git a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java new file mode 100644 index 000000000000..d4999ab8be0a --- /dev/null +++ b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 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.location; + +import android.annotation.Nullable; +import android.content.Context; +import android.location.Location; +import android.location.LocationManager; +import android.os.Binder; + +import com.android.internal.util.Preconditions; +import com.android.server.location.util.Injector; + +class PassiveLocationProviderManager extends LocationProviderManager { + + PassiveLocationProviderManager(Context context, Injector injector) { + super(context, injector, LocationManager.PASSIVE_PROVIDER, null); + } + + @Override + public void setRealProvider(AbstractLocationProvider provider) { + Preconditions.checkArgument(provider instanceof PassiveProvider); + super.setRealProvider(provider); + } + + @Override + public void setMockProvider(@Nullable MockProvider provider) { + if (provider != null) { + throw new IllegalArgumentException("Cannot mock the passive provider"); + } + } + + public void updateLocation(Location location) { + synchronized (mLock) { + PassiveProvider passiveProvider = (PassiveProvider) mProvider.getProvider(); + Preconditions.checkState(passiveProvider != null); + + long identity = Binder.clearCallingIdentity(); + try { + passiveProvider.updateLocation(location); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } +} diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java index 0b7968be484b..1b599b026c38 100644 --- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java @@ -16,17 +16,21 @@ package com.android.server.location.gnss; +import static android.location.LocationManager.GPS_PROVIDER; + import static com.android.server.location.LocationPermissions.PERMISSION_FINE; import static com.android.server.location.gnss.GnssManagerService.TAG; import android.annotation.Nullable; import android.location.LocationManagerInternal; +import android.location.LocationManagerInternal.ProviderEnabledListener; import android.location.util.identity.CallerIdentity; import android.os.IBinder; import android.os.IInterface; import android.os.Process; import android.util.ArraySet; +import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.location.listeners.BinderListenerRegistration; import com.android.server.location.listeners.ListenerMultiplexer; @@ -161,8 +165,8 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter protected final LocationManagerInternal mLocationManagerInternal; private final UserListener mUserChangedListener = this::onUserChanged; - private final SettingsHelper.UserSettingChangedListener mLocationEnabledChangedListener = - this::onLocationEnabledChanged; + private final ProviderEnabledListener mProviderEnabledChangedListener = + this::onProviderEnabledChanged; private final SettingsHelper.GlobalSettingChangedListener mBackgroundThrottlePackageWhitelistChangedListener = this::onBackgroundThrottlePackageWhitelistChanged; @@ -233,12 +237,11 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter } CallerIdentity identity = registration.getIdentity(); - // TODO: this should be checking if the gps provider is enabled, not if location is enabled, - // but this is the same for now. return registration.isPermitted() && (registration.isForeground() || isBackgroundRestrictionExempt(identity)) && mUserInfoHelper.isCurrentUserId(identity.getUserId()) - && mSettingsHelper.isLocationEnabled(identity.getUserId()) + && mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER, + identity.getUserId()) && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), identity.getPackageName()); } @@ -263,7 +266,8 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter } mUserInfoHelper.addListener(mUserChangedListener); - mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener); + mLocationManagerInternal.addProviderEnabledListener(GPS_PROVIDER, + mProviderEnabledChangedListener); mSettingsHelper.addOnBackgroundThrottlePackageWhitelistChangedListener( mBackgroundThrottlePackageWhitelistChangedListener); mSettingsHelper.addOnLocationPackageBlacklistChangedListener( @@ -279,7 +283,8 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter } mUserInfoHelper.removeListener(mUserChangedListener); - mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener); + mLocationManagerInternal.removeProviderEnabledListener(GPS_PROVIDER, + mProviderEnabledChangedListener); mSettingsHelper.removeOnBackgroundThrottlePackageWhitelistChangedListener( mBackgroundThrottlePackageWhitelistChangedListener); mSettingsHelper.removeOnLocationPackageBlacklistChangedListener( @@ -294,7 +299,8 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter } } - private void onLocationEnabledChanged(int userId) { + private void onProviderEnabledChanged(String provider, int userId, boolean enabled) { + Preconditions.checkState(GPS_PROVIDER.equals(provider)); updateRegistrations(registration -> registration.getIdentity().getUserId() == userId); } diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java index e17cca423822..cea5a69526c6 100644 --- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java +++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java @@ -88,7 +88,7 @@ class GnssNetworkConnectivityHandler { // Default time limit in milliseconds for the ConnectivityManager to find a suitable // network with SUPL connectivity or report an error. - private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 10 * 1000; + private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 20 * 1000; private static final int HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS = 5; diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java index 528cf8acd5b3..f94de9be0cfe 100644 --- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java @@ -104,18 +104,18 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, * should return true if a matching call to {@link #unregisterWithService()} is required to * unregister (ie, if registration succeeds). * - * @see #reregisterWithService(Object) + * @see #reregisterWithService(Object, Object) */ - protected abstract boolean registerWithService(TMergedRequest mergedRequest); + protected abstract boolean registerWithService(TMergedRequest newRequest); /** * Invoked when the service already has a request, and it is being replaced with a new request. * The default implementation unregisters first, then registers with the new merged request, but * this may be overridden by subclasses in order to reregister more efficiently. */ - protected boolean reregisterWithService(TMergedRequest mergedRequest) { + protected boolean reregisterWithService(TMergedRequest oldRequest, TMergedRequest newRequest) { unregisterWithService(); - return registerWithService(mergedRequest); + return registerWithService(newRequest); } /** @@ -368,6 +368,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, mCurrentRequest = null; if (mServiceRegistered) { mServiceRegistered = false; + mCurrentRequest = null; unregisterWithService(); } return; @@ -376,11 +377,15 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, TMergedRequest merged = mergeRequests(actives); if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) { if (mServiceRegistered) { - mServiceRegistered = reregisterWithService(merged); + mServiceRegistered = reregisterWithService(mCurrentRequest, merged); } else { mServiceRegistered = registerWithService(merged); } - mCurrentRequest = merged; + if (mServiceRegistered) { + mCurrentRequest = merged; + } else { + mCurrentRequest = null; + } } } finally { Binder.restoreCallingIdentity(identity); @@ -389,29 +394,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, } /** - * Evaluates the given predicate for all registrations, and forces an {@link #updateService()} - * if any predicate returns true for an active registration. The predicate will always be - * evaluated for all registrations, even inactive registrations, or if it has already returned - * true for a prior registration. - */ - protected final void updateService(Predicate<TRegistration> predicate) { - synchronized (mRegistrations) { - boolean updateService = false; - final int size = mRegistrations.size(); - for (int i = 0; i < size; i++) { - TRegistration registration = mRegistrations.valueAt(i); - if (predicate.test(registration) && registration.isActive()) { - updateService = true; - } - } - - if (updateService) { - updateService(); - } - } - } - - /** * Begins buffering calls to {@link #updateService()} until {@link UpdateServiceLock#close()} * is called. This is useful to prevent extra work when combining multiple calls (for example, * buffering {@code updateService()} until after multiple adds/removes/updates occur. diff --git a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java index 0bdd1316d265..ac56c51568be 100644 --- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; import android.location.util.identity.CallerIdentity; import android.os.Process; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.listeners.ListenerExecutor; import com.android.server.FgThread; @@ -39,6 +40,9 @@ import java.util.concurrent.Executor; */ public class ListenerRegistration<TRequest, TListener> implements ListenerExecutor { + @VisibleForTesting + public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor(); + private final Executor mExecutor; private final @Nullable TRequest mRequest; private final CallerIdentity mIdentity; @@ -55,9 +59,9 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut // there's a slight loophole here for pending intents - pending intent callbacks can // always be run on the direct executor since they're always asynchronous, but honestly // you shouldn't be using pending intent callbacks within the same process anyways - mExecutor = FgThread.getExecutor(); + mExecutor = IN_PROCESS_EXECUTOR; } else { - mExecutor = DIRECT_EXECUTOR; + mExecutor = DIRECT_EXECUTOR; } mRequest = request; @@ -73,7 +77,7 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut /** * Returns the request associated with this listener, or null if one wasn't supplied. */ - public final @Nullable TRequest getRequest() { + public @Nullable TRequest getRequest() { return mRequest; } @@ -107,7 +111,7 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut */ protected void onInactive() {} - final boolean isActive() { + public final boolean isActive() { return mActive; } @@ -120,7 +124,7 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut return false; } - final boolean isRegistered() { + public final boolean isRegistered() { return mListener != null; } diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java index 6a815ead9f9f..0698cca903f0 100644 --- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java @@ -39,7 +39,7 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends protected RemovableListenerRegistration(String tag, @Nullable TRequest request, CallerIdentity callerIdentity, TListener listener) { super(request, callerIdentity, listener); - mTag = tag; + mTag = Objects.requireNonNull(tag); } /** diff --git a/services/core/java/com/android/server/media/HandlerExecutor.java b/services/core/java/com/android/server/media/HandlerExecutor.java new file mode 100644 index 000000000000..7c9e72bcf384 --- /dev/null +++ b/services/core/java/com/android/server/media/HandlerExecutor.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 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.media; + +import android.annotation.NonNull; +import android.os.Handler; + +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; + +/** + * An adapter {@link Executor} that posts all executed tasks onto the given + * {@link Handler}. + * + * @hide + */ +public class HandlerExecutor implements Executor { + private final Handler mHandler; + + public HandlerExecutor(@NonNull Handler handler) { + mHandler = Objects.requireNonNull(handler); + } + + @Override + public void execute(Runnable command) { + if (!mHandler.post(command)) { + throw new RejectedExecutionException(mHandler + " is shutting down"); + } + } +} diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java index 5d1b74912546..162c388dadd1 100644 --- a/services/core/java/com/android/server/media/MediaSession2Record.java +++ b/services/core/java/com/android/server/media/MediaSession2Record.java @@ -20,7 +20,6 @@ import android.media.MediaController2; import android.media.Session2CommandGroup; import android.media.Session2Token; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.Looper; import android.os.ResultReceiver; import android.os.UserHandle; diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 249b6801758b..07527c2a15d8 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -270,11 +270,12 @@ public abstract class ApexManager { abstract boolean revertActiveSessions(); /** - * Abandons the staged session with the given sessionId. + * Abandons the staged session with the given sessionId. Client should handle {@code false} + * return value carefully as failure here can leave device in inconsistent state. * - * @return {@code true} upon success, {@code false} if any remote exception occurs + * @return {@code true} upon success, {@code false} if any exception occurs */ - abstract boolean abortStagedSession(int sessionId) throws PackageManagerException; + abstract boolean abortStagedSession(int sessionId); /** * Uninstalls given {@code apexPackage}. @@ -753,17 +754,13 @@ public abstract class ApexManager { } @Override - boolean abortStagedSession(int sessionId) throws PackageManagerException { + boolean abortStagedSession(int sessionId) { try { waitForApexService().abortStagedSession(sessionId); return true; - } catch (RemoteException re) { - Slog.e(TAG, "Unable to contact apexservice", re); - return false; } catch (Exception e) { - throw new PackageManagerException( - PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, - "Failed to abort staged session : " + e.getMessage()); + Slog.e(TAG, e.getMessage(), e); + return false; } } @@ -1122,7 +1119,7 @@ public abstract class ApexManager { } @Override - boolean abortStagedSession(int sessionId) throws PackageManagerException { + boolean abortStagedSession(int sessionId) { throw new UnsupportedOperationException(); } diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS index fe6aad70c31e..e48862e2e5e0 100644 --- a/services/core/java/com/android/server/pm/OWNERS +++ b/services/core/java/com/android/server/pm/OWNERS @@ -14,16 +14,16 @@ per-file ApexManager.java = dariofreni@google.com, ioffe@google.com, olilan@goog per-file StagingManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com # dex -per-file AbstractStatsBase.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file BackgroundDexOptService.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file CompilerStats.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file DynamicCodeLoggingService.java = alanstokes@google.com, agampe@google.com, calin@google.com, ngeoffray@google.com -per-file InstructionSets.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file OtaDexoptService.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file OtaDexoptShellCommand.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file PackageDexOptimizer.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file PackageManagerServiceCompilerMapping.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file PackageUsage.java = agampe@google.com, calin@google.com, ngeoffray@google.com +per-file AbstractStatsBase.java = calin@google.com, ngeoffray@google.com +per-file BackgroundDexOptService.java = calin@google.com, ngeoffray@google.com +per-file CompilerStats.java = calin@google.com, ngeoffray@google.com +per-file DynamicCodeLoggingService.java = alanstokes@google.com, calin@google.com, ngeoffray@google.com +per-file InstructionSets.java = calin@google.com, ngeoffray@google.com +per-file OtaDexoptService.java = calin@google.com, ngeoffray@google.com +per-file OtaDexoptShellCommand.java = calin@google.com, ngeoffray@google.com +per-file PackageDexOptimizer.java = calin@google.com, ngeoffray@google.com +per-file PackageManagerServiceCompilerMapping.java = calin@google.com, ngeoffray@google.com +per-file PackageUsage.java = calin@google.com, ngeoffray@google.com # multi user / cross profile per-file CrossProfileAppsServiceImpl.java = omakoto@google.com, yamasani@google.com diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 312dcddd577d..55e7ca8ca838 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -1284,10 +1284,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements int N = mSessions.size(); for (int i = 0; i < N; i++) { final PackageInstallerSession session = mSessions.valueAt(i); - if (session.isStagedAndInTerminalState()) { + + // Do not print finalized staged session as active install sessions + final PackageInstallerSession rootSession = session.hasParentSessionId() + ? getSession(session.getParentSessionId()) + : session; + if (rootSession.isStagedAndInTerminalState()) { finalizedSessions.add(session); continue; } + session.dump(pw); pw.println(); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 7765f18300fe..05026a0cafb6 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -169,7 +169,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final int MSG_STREAM_VALIDATE_AND_COMMIT = 2; private static final int MSG_INSTALL = 3; private static final int MSG_ON_PACKAGE_INSTALLED = 4; - private static final int MSG_SESSION_VERIFICATION_FAILURE = 5; + private static final int MSG_SESSION_VALIDATION_FAILURE = 5; /** XML constants used for persisting a session */ static final String TAG_SESSION = "session"; @@ -475,10 +475,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { packageName, returnCode, message, extras); break; - case MSG_SESSION_VERIFICATION_FAILURE: + case MSG_SESSION_VALIDATION_FAILURE: final int error = msg.arg1; final String detailMessage = (String) msg.obj; - onSessionVerificationFailure(error, detailMessage); + onSessionValidationFailure(error, detailMessage); break; } @@ -1246,14 +1246,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // the parent if (unrecoverableFailure != null) { // {@link #streamValidateAndCommit()} calls - // {@link #onSessionVerificationFailure(PackageManagerException)}, but we don't + // {@link #onSessionValidationFailure(PackageManagerException)}, but we don't // expect it to ever do so for parent sessions. Call that on this parent to clean // it up and notify listeners of the error. - onSessionVerificationFailure(unrecoverableFailure); + onSessionValidationFailure(unrecoverableFailure); // fail other child sessions that did not already fail for (int i = nonFailingSessions.size() - 1; i >= 0; --i) { PackageInstallerSession session = nonFailingSessions.get(i); - session.onSessionVerificationFailure(unrecoverableFailure); + session.onSessionValidationFailure(unrecoverableFailure); } } } @@ -1575,11 +1575,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { assertMultiPackageConsistencyLocked(childSessions); } } catch (PackageManagerException e) { - throw onSessionVerificationFailure(e); + throw onSessionValidationFailure(e); } catch (Throwable e) { // Convert all exceptions into package manager exceptions as only those are handled // in the code above. - throw onSessionVerificationFailure(new PackageManagerException(e)); + throw onSessionValidationFailure(new PackageManagerException(e)); } } @@ -1613,20 +1613,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } return true; } catch (PackageManagerException e) { - throw onSessionVerificationFailure(e); + throw onSessionValidationFailure(e); } catch (Throwable e) { // Convert all exceptions into package manager exceptions as only those are handled // in the code above. - throw onSessionVerificationFailure(new PackageManagerException(e)); + throw onSessionValidationFailure(new PackageManagerException(e)); } } - private PackageManagerException onSessionVerificationFailure(PackageManagerException e) { - onSessionVerificationFailure(e.error, ExceptionUtils.getCompleteMessage(e)); + private PackageManagerException onSessionValidationFailure(PackageManagerException e) { + onSessionValidationFailure(e.error, ExceptionUtils.getCompleteMessage(e)); return e; } - private void onSessionVerificationFailure(int error, String detailMessage) { + private void onSessionValidationFailure(int error, String detailMessage) { // Session is sealed but could not be verified, we need to destroy it. destroyInternal(); // Dispatch message to remove session from PackageInstallerService. @@ -2091,7 +2091,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (ps == null) { return 0; } - final File apkDirOrPath = ps.codePath; + final File apkDirOrPath = ps.getCodePath(); if (apkDirOrPath == null) { return 0; } @@ -2990,7 +2990,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { mDataLoaderFinished = true; } - dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, + dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, "Failure to obtain data loader"); return; } @@ -3040,7 +3040,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { mDataLoaderFinished = true; } - dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, + dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, "Failed to prepare image."); if (manualStartAndDestroy) { dataLoader.destroy(dataLoaderId); @@ -3061,7 +3061,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { mDataLoaderFinished = true; } - dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, + dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, "DataLoader reported unrecoverable failure."); break; } @@ -3117,7 +3117,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { mDataLoaderFinished = true; } - dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, + dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, "Image is missing pages required for installation."); break; } @@ -3142,8 +3142,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return false; } - private void dispatchSessionVerificationFailure(int error, String detailMessage) { - mHandler.obtainMessage(MSG_SESSION_VERIFICATION_FAILURE, error, -1, + private void dispatchSessionValidationFailure(int error, String detailMessage) { + mHandler.obtainMessage(MSG_SESSION_VALIDATION_FAILURE, error, -1, detailMessage).sendToTarget(); } @@ -3321,7 +3321,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** {@hide} */ void setStagedSessionReady() { synchronized (mLock) { - if (mDestroyed) return; // Do not allow destroyed staged session to change state + // Do not allow destroyed/failed staged session to change state + if (mDestroyed || mStagedSessionFailed) return; mStagedSessionReady = true; mStagedSessionApplied = false; mStagedSessionFailed = false; @@ -3332,10 +3333,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } /** {@hide} */ - void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, - String errorMessage) { + void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) { synchronized (mLock) { - if (mDestroyed) return; // Do not allow destroyed staged session to change state + // Do not allow destroyed/failed staged session to change state + if (mDestroyed || mStagedSessionFailed) return; mStagedSessionReady = false; mStagedSessionApplied = false; mStagedSessionFailed = true; @@ -3350,7 +3351,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** {@hide} */ void setStagedSessionApplied() { synchronized (mLock) { - if (mDestroyed) return; // Do not allow destroyed staged session to change state + // Do not allow destroyed/failed staged session to change state + if (mDestroyed || mStagedSessionFailed) return; mStagedSessionReady = false; mStagedSessionApplied = true; mStagedSessionFailed = false; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a726c8d26b98..58a1648e51ad 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3010,7 +3010,7 @@ public class PackageManagerService extends IPackageManager.Stub final int packageSettingCount = mSettings.mPackages.size(); for (int i = packageSettingCount - 1; i >= 0; i--) { PackageSetting ps = mSettings.mPackages.valueAt(i); - if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists()) + if (!isExternal(ps) && (ps.getCodePath() == null || !ps.getCodePath().exists()) && mSettings.getDisabledSystemPkgLPr(ps.name) != null) { mSettings.mPackages.removeAt(i); mSettings.enableSystemPackageLPw(ps.name); @@ -3175,11 +3175,11 @@ public class PackageManagerService extends IPackageManager.Stub logCriticalInfo(Log.WARN, "Expecting better updated system app for " + ps.name + "; removing system app. Last known" - + " codePath=" + ps.codePathString + + " codePath=" + ps.getCodePathString() + ", versionCode=" + ps.versionCode + "; scanned versionCode=" + scannedPkg.getLongVersionCode()); removePackageLI(scannedPkg, true); - mExpectingBetter.put(ps.name, ps.codePath); + mExpectingBetter.put(ps.name, ps.getCodePath()); } continue; @@ -3202,14 +3202,14 @@ public class PackageManagerService extends IPackageManager.Stub // code path, but, changes the package name. final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name); - if (disabledPs.codePath == null || !disabledPs.codePath.exists() + if (disabledPs.getCodePath() == null || !disabledPs.getCodePath().exists() || disabledPs.pkg == null) { possiblyDeletedUpdatedSystemApps.add(ps.name); } else { // We're expecting that the system app should remain disabled, but add // it to expecting better to recover in case the data version cannot // be scanned. - mExpectingBetter.put(disabledPs.name, disabledPs.codePath); + mExpectingBetter.put(disabledPs.name, disabledPs.getCodePath()); } } } @@ -8551,6 +8551,15 @@ public class PackageManagerService extends IPackageManager.Stub if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { + if (listFactory) { + if (!ps.isSystem()) { + continue; + } + PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps); + if (psDisabled != null) { + ps = psDisabled; + } + } if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } @@ -8565,7 +8574,16 @@ public class PackageManagerService extends IPackageManager.Stub } else { list = new ArrayList<>(mPackages.size()); for (AndroidPackage p : mPackages.values()) { - final PackageSetting ps = getPackageSetting(p.getPackageName()); + PackageSetting ps = getPackageSetting(p.getPackageName()); + if (listFactory) { + if (!p.isSystem()) { + continue; + } + PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps); + if (psDisabled != null) { + ps = psDisabled; + } + } if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } @@ -9150,7 +9168,7 @@ public class PackageManagerService extends IPackageManager.Stub : getLastModifiedTime(parsedPackage); final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage); if (ps != null && !forceCollect - && ps.codePathString.equals(parsedPackage.getCodePath()) + && ps.getCodePathString().equals(parsedPackage.getCodePath()) && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(settingsVersionForPackage) && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) { @@ -9383,8 +9401,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - final boolean newPkgChangedPaths = - pkgAlreadyExists && !pkgSetting.codePathString.equals(parsedPackage.getCodePath()); + final boolean newPkgChangedPaths = pkgAlreadyExists + && !pkgSetting.getCodePathString().equals(parsedPackage.getCodePath()); final boolean newPkgVersionGreater = pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode; final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated @@ -9403,11 +9421,11 @@ public class PackageManagerService extends IPackageManager.Stub "System package updated;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode() - + "; " + pkgSetting.codePathString + " --> " + parsedPackage.getCodePath()); + + "; " + pkgSetting.getCodePathString() + + " --> " + parsedPackage.getCodePath()); final InstallArgs args = createInstallArgsForExisting( - pkgSetting.codePathString, - pkgSetting.resourcePathString, getAppDexInstructionSets( + pkgSetting.getCodePathString(), getAppDexInstructionSets( pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString)); args.cleanUpResourcesLI(); synchronized (mLock) { @@ -9482,11 +9500,10 @@ public class PackageManagerService extends IPackageManager.Stub + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode() - + "; " + pkgSetting.codePathString + " --> " + + "; " + pkgSetting.getCodePathString() + " --> " + parsedPackage.getCodePath()); InstallArgs args = createInstallArgsForExisting( - pkgSetting.codePathString, - pkgSetting.resourcePathString, getAppDexInstructionSets( + pkgSetting.getCodePathString(), getAppDexInstructionSets( pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); @@ -9499,7 +9516,7 @@ public class PackageManagerService extends IPackageManager.Stub logCriticalInfo(Log.INFO, "System package disabled;" + " name: " + pkgSetting.name - + "; old: " + pkgSetting.codePathString + " @ " + + "; old: " + pkgSetting.getCodePathString() + " @ " + pkgSetting.versionCode + "; new: " + parsedPackage.getCodePath() + " @ " + parsedPackage.getCodePath()); @@ -11325,7 +11342,7 @@ public class PackageManagerService extends IPackageManager.Stub if (changedAbiCodePath == null) { changedAbiCodePath = new ArrayList<>(); } - changedAbiCodePath.add(ps.codePathString); + changedAbiCodePath.add(ps.getCodePathString()); } } } @@ -11421,7 +11438,6 @@ public class PackageManagerService extends IPackageManager.Stub // Initialize package source and resource directories final File destCodeFile = new File(parsedPackage.getCodePath()); - final File destResourceFile = new File(parsedPackage.getCodePath()); // We keep references to the derived CPU Abis from settings in oder to reuse // them in the case where we're not upgrading or booting for the first time. @@ -11479,7 +11495,7 @@ public class PackageManagerService extends IPackageManager.Stub // REMOVE SharedUserSetting from method; update in a separate call pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(), originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting, - destCodeFile, destResourceFile, parsedPackage.getNativeLibraryRootDir(), + destCodeFile, parsedPackage.getNativeLibraryRootDir(), AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage), AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage), parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user, @@ -11497,7 +11513,7 @@ public class PackageManagerService extends IPackageManager.Stub // secondaryCpuAbi are not known at this point so we always update them // to null here, only to reset them at a later point. Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting, - destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(), + destCodeFile, parsedPackage.getNativeLibraryDir(), AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting), AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting), PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting), @@ -12066,15 +12082,13 @@ public class PackageManagerService extends IPackageManager.Stub if (known != null) { if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Examining " + pkg.getCodePath() - + " and requiring known paths " + known.codePathString - + " & " + known.resourcePathString); + + " and requiring known path " + known.getCodePathString()); } - if (!pkg.getCodePath().equals(known.codePathString) - || !pkg.getCodePath().equals(known.resourcePathString)) { + if (!pkg.getCodePath().equals(known.getCodePathString())) { throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED, "Application package " + pkg.getPackageName() + " found at " + pkg.getCodePath() - + " but expected at " + known.codePathString + + " but expected at " + known.getCodePathString() + "; ignoring."); } } else { @@ -15586,9 +15600,8 @@ public class PackageManagerService extends IPackageManager.Stub * Create args that describe an existing installed package. Typically used * when cleaning up old installs, or used as a move source. */ - private InstallArgs createInstallArgsForExisting(String codePath, - String resourcePath, String[] instructionSets) { - return new FileInstallArgs(codePath, resourcePath, instructionSets); + private InstallArgs createInstallArgsForExisting(String codePath, String[] instructionSets) { + return new FileInstallArgs(codePath, instructionSets); } static abstract class InstallArgs { @@ -15669,10 +15682,8 @@ public class PackageManagerService extends IPackageManager.Stub abstract boolean doRename(int status, ParsedPackage parsedPackage); abstract int doPostInstall(int status, int uid); - /** @see PackageSettingBase#codePathString */ + /** @see PackageSettingBase#getCodePath() */ abstract String getCodePath(); - /** @see PackageSettingBase#resourcePathString */ - abstract String getResourcePath(); // Need installer lock especially for dex file removal. abstract void cleanUpResourcesLI(); @@ -15743,14 +15754,13 @@ public class PackageManagerService extends IPackageManager.Stub } /** Existing install */ - FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) { + FileInstallArgs(String codePath, String[] instructionSets) { super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY, null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0, PackageParser.SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN, false, DataLoaderType.NONE); this.codeFile = (codePath != null) ? new File(codePath) : null; - this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; } int copyApk() { @@ -15766,7 +15776,6 @@ public class PackageManagerService extends IPackageManager.Stub if (origin.staged) { if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy"); codeFile = origin.file; - resourceFile = origin.file; return PackageManager.INSTALL_SUCCEEDED; } @@ -15775,7 +15784,6 @@ public class PackageManagerService extends IPackageManager.Stub final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral); codeFile = tempDir; - resourceFile = tempDir; } catch (IOException e) { Slog.w(TAG, "Failed to create copy file: " + e); return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; @@ -15846,7 +15854,6 @@ public class PackageManagerService extends IPackageManager.Stub // Reflect the rename internally codeFile = afterCodeFile; - resourceFile = afterCodeFile; // Reflect the rename in scanned details try { @@ -15875,11 +15882,6 @@ public class PackageManagerService extends IPackageManager.Stub return (codeFile != null) ? codeFile.getAbsolutePath() : null; } - @Override - String getResourcePath() { - return (resourceFile != null) ? resourceFile.getAbsolutePath() : null; - } - private boolean cleanUp() { if (codeFile == null || !codeFile.exists()) { return false; @@ -15892,10 +15894,6 @@ public class PackageManagerService extends IPackageManager.Stub removeCodePathLI(codeFile); - if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) { - resourceFile.delete(); - } - return true; } @@ -15927,7 +15925,6 @@ public class PackageManagerService extends IPackageManager.Stub */ class MoveInstallArgs extends InstallArgs { private File codeFile; - private File resourceFile; /** New install */ MoveInstallArgs(InstallParams params) { @@ -15950,7 +15947,6 @@ public class PackageManagerService extends IPackageManager.Stub final String toPathName = new File(move.fromCodePath).getName(); codeFile = new File(Environment.getDataAppDirectory(move.toUuid), toPathName); - resourceFile = codeFile; if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + codeFile); return PackageManager.INSTALL_SUCCEEDED; @@ -15987,11 +15983,6 @@ public class PackageManagerService extends IPackageManager.Stub return (codeFile != null) ? codeFile.getAbsolutePath() : null; } - @Override - String getResourcePath() { - return (resourceFile != null) ? resourceFile.getAbsolutePath() : null; - } - private boolean cleanUp(String volumeUuid) { final String toPathName = new File(move.fromCodePath).getName(); final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid), @@ -16777,7 +16768,6 @@ public class PackageManagerService extends IPackageManager.Stub // installed. We need to make sure to delete the older one's .apk. res.removedInfo.args = createInstallArgsForExisting( oldPackage.getCodePath(), - oldPackage.getCodePath(), getAppDexInstructionSets( AndroidPackageUtils.getPrimaryCpuAbi(oldPackage, deletedPkgSetting), @@ -18558,7 +18548,6 @@ public class PackageManagerService extends IPackageManager.Stub // user handle installed state int[] allUsers; /** enabled state of the uninstalled application */ - final int origEnabledState; synchronized (mLock) { uninstalledPs = mSettings.mPackages.get(packageName); if (uninstalledPs == null) { @@ -18574,10 +18563,6 @@ public class PackageManagerService extends IPackageManager.Stub } disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName); - // Save the enabled state before we delete the package. When deleting a stub - // application we always set the enabled state to 'disabled'. - origEnabledState = uninstalledPs == null - ? COMPONENT_ENABLED_STATE_DEFAULT : uninstalledPs.getEnabled(userId); // Static shared libs can be declared by any package, so let us not // allow removing a package if it provides a lib others depend on. pkg = mPackages.get(packageName); @@ -18656,20 +18641,32 @@ public class PackageManagerService extends IPackageManager.Stub if (stubPkg != null && stubPkg.isStub()) { final PackageSetting stubPs; synchronized (mLock) { - // restore the enabled state of the stub; the state is overwritten when - // the stub is uninstalled stubPs = mSettings.getPackageLPr(stubPkg.getPackageName()); - if (stubPs != null) { - stubPs.setEnabled(origEnabledState, userId, "android"); - } } - if (origEnabledState == COMPONENT_ENABLED_STATE_DEFAULT - || origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) { - if (DEBUG_COMPRESSION) { - Slog.i(TAG, "Enabling system stub after removal; pkg: " - + stubPkg.getPackageName()); + + if (stubPs != null) { + boolean enable = false; + for (int aUserId : allUsers) { + if (stubPs.getInstalled(aUserId)) { + int enabled = stubPs.getEnabled(aUserId); + if (enabled == COMPONENT_ENABLED_STATE_DEFAULT + || enabled == COMPONENT_ENABLED_STATE_ENABLED) { + enable = true; + break; + } + } + } + + if (enable) { + if (DEBUG_COMPRESSION) { + Slog.i(TAG, "Enabling system stub after removal; pkg: " + + stubPkg.getPackageName()); + } + enableCompressedPackage(stubPkg, stubPs); + } else if (DEBUG_COMPRESSION) { + Slog.i(TAG, "System stub disabled for all users, leaving uncompressed " + + "after removal; pkg: " + stubPkg.getPackageName()); } - enableCompressedPackage(stubPkg, stubPs); } } } @@ -18998,7 +18995,7 @@ public class PackageManagerService extends IPackageManager.Stub // Install the system package if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); try { - installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles, + installPackageFromSystemLIF(disabledPs.getCodePathString(), allUserHandles, outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings); } catch (PackageManagerException e) { @@ -19013,8 +19010,15 @@ public class PackageManagerService extends IPackageManager.Stub // and re-enable it afterward. final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName()); if (stubPs != null) { - stubPs.setEnabled( - COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); + int userId = action.user == null + ? UserHandle.USER_ALL : action.user.getIdentifier(); + if (userId == UserHandle.USER_ALL) { + for (int aUserId : allUserHandles) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android"); + } + } else if (userId >= UserHandle.USER_SYSTEM) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android"); + } } } } @@ -19123,7 +19127,7 @@ public class PackageManagerService extends IPackageManager.Stub // Delete application code and resources only for parent packages if (deleteCodeAndResources && (outInfo != null)) { outInfo.args = createInstallArgsForExisting( - ps.codePathString, ps.resourcePathString, getAppDexInstructionSets( + ps.getCodePathString(), getAppDexInstructionSets( ps.primaryCpuAbiString, ps.secondaryCpuAbiString)); if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args); } @@ -19660,7 +19664,7 @@ public class PackageManagerService extends IPackageManager.Stub final String[] packageNames = { packageName }; final long[] ceDataInodes = { ps.getCeDataInode(userId) }; - final String[] codePaths = { ps.codePathString }; + final String[] codePaths = { ps.getCodePathString() }; try { mInstaller.getAppSize(ps.volumeUuid, packageNames, userId, 0, @@ -22582,11 +22586,11 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mInstallLock) { final AndroidPackage pkg; try { - pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null); + pkg = scanPackageTracedLI(ps.getCodePath(), parseFlags, SCAN_INITIAL, 0, null); loaded.add(pkg); } catch (PackageManagerException e) { - Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage()); + Slog.w(TAG, "Failed to scan " + ps.getCodePath() + ": " + e.getMessage()); } if (!Build.FINGERPRINT.equals(ver.fingerprint)) { @@ -22657,33 +22661,33 @@ public class PackageManagerService extends IPackageManager.Stub final ArrayList<AndroidPackage> unloaded = new ArrayList<>(); synchronized (mInstallLock) { - synchronized (mLock) { - final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid); - for (PackageSetting ps : packages) { - if (ps.pkg == null) continue; - - final AndroidPackage pkg = ps.pkg; - final int deleteFlags = PackageManager.DELETE_KEEP_DATA; - final PackageRemovedInfo outInfo = new PackageRemovedInfo(this); - - try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags, - "unloadPrivatePackagesInner")) { - if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo, - false, null)) { - unloaded.add(pkg); - } else { - Slog.w(TAG, "Failed to unload " + ps.codePath); + synchronized (mLock) { + final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid); + for (PackageSetting ps : packages) { + if (ps.pkg == null) continue; + + final AndroidPackage pkg = ps.pkg; + final int deleteFlags = PackageManager.DELETE_KEEP_DATA; + final PackageRemovedInfo outInfo = new PackageRemovedInfo(this); + + try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags, + "unloadPrivatePackagesInner")) { + if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo, + false, null)) { + unloaded.add(pkg); + } else { + Slog.w(TAG, "Failed to unload " + ps.getCodePath()); + } } + + // Try very hard to release any references to this package + // so we don't risk the system server being killed due to + // open FDs + AttributeCache.instance().removePackage(ps.name); } - // Try very hard to release any references to this package - // so we don't risk the system server being killed due to - // open FDs - AttributeCache.instance().removePackage(ps.name); + mSettings.writeLPr(); } - - mSettings.writeLPr(); - } } if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded); @@ -22726,7 +22730,7 @@ public class PackageManagerService extends IPackageManager.Stub final int packageCount = mSettings.mPackages.size(); for (int i = 0; i < packageCount; i++) { final PackageSetting ps = mSettings.mPackages.valueAt(i); - codePaths.add(ps.codePath.getAbsolutePath()); + codePaths.add(ps.getCodePath().getAbsolutePath()); } return codePaths; } @@ -23643,8 +23647,20 @@ public class PackageManagerService extends IPackageManager.Stub } } - void onNewUserCreated(final int userId) { - mPermissionManager.onNewUserCreated(userId); + void onNewUserCreated(@UserIdInt int userId, boolean convertedFromPreCreated) { + if (DEBUG_PERMISSIONS) { + Slog.d(TAG, "onNewUserCreated(id=" + userId + + ", convertedFromPreCreated=" + convertedFromPreCreated + ")"); + } + if (!convertedFromPreCreated) { + mPermissionManager.onNewUserCreated(userId); + return; + } + if (!readPermissionStateForUser(userId)) { + // Could not read the existing permissions, re-grant them. + Slog.i(TAG, "re-granting permissions for pre-created user " + userId); + mPermissionManager.onNewUserCreated(userId); + } } boolean readPermissionStateForUser(@UserIdInt int userId) { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 668f375e2e9b..7aeec6d68d26 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -423,13 +423,15 @@ class PackageManagerShellCommand extends ShellCommand { final List<ApplicationInfo> list; if (packageName == null) { final ParceledListSlice<ApplicationInfo> packages = - mInterface.getInstalledApplications( - PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); + mInterface.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY + | PackageManager.MATCH_UNINSTALLED_PACKAGES, + UserHandle.USER_SYSTEM); list = packages.getList(); } else { list = new ArrayList<>(1); - list.add(mInterface.getApplicationInfo(packageName, - PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM)); + list.add(mInterface.getApplicationInfo(packageName, PackageManager.MATCH_SYSTEM_ONLY + | PackageManager.MATCH_UNINSTALLED_PACKAGES, + UserHandle.USER_SYSTEM)); } for (ApplicationInfo info : list) { if (info.isUpdatedSystemApp()) { diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 432d7f335ebc..a3a727367c56 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -71,13 +71,13 @@ public class PackageSetting extends PackageSettingBase { private PackageStateUnserialized pkgState = new PackageStateUnserialized(); @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public PackageSetting(String name, String realName, File codePath, File resourcePath, + public PackageSetting(String name, String realName, @NonNull File codePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, long pVersionCode, int pkgFlags, int privateFlags, int sharedUserId, String[] usesStaticLibraries, long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups) { - super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString, + super(name, realName, codePath, legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode, pkgFlags, privateFlags, usesStaticLibraries, usesStaticLibrariesVersions); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 834303cc14c6..6010344b8c65 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -64,10 +64,8 @@ public abstract class PackageSettingBase extends SettingBase { * this is path to single base APK file; for cluster packages this is * path to the cluster directory. */ - File codePath; - String codePathString; - File resourcePath; - String resourcePathString; + private File mCodePath; + private String mCodePathString; String[] usesStaticLibraries; long[] usesStaticLibrariesVersions; @@ -138,7 +136,7 @@ public abstract class PackageSettingBase extends SettingBase { boolean forceQueryableOverride; - PackageSettingBase(String name, String realName, File codePath, File resourcePath, + PackageSettingBase(String name, String realName, @NonNull File codePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, long pVersionCode, int pkgFlags, int pkgPrivateFlags, @@ -148,10 +146,7 @@ public abstract class PackageSettingBase extends SettingBase { this.realName = realName; this.usesStaticLibraries = usesStaticLibraries; this.usesStaticLibrariesVersions = usesStaticLibrariesVersions; - this.codePath = codePath; - this.codePathString = codePath.toString(); - this.resourcePath = resourcePath; - this.resourcePathString = resourcePath.toString(); + setCodePath(codePath); this.legacyNativeLibraryPathString = legacyNativeLibraryPathString; this.primaryCpuAbiString = primaryCpuAbiString; this.secondaryCpuAbiString = secondaryCpuAbiString; @@ -235,8 +230,7 @@ public abstract class PackageSettingBase extends SettingBase { } private void doCopy(PackageSettingBase orig) { - codePath = orig.codePath; - codePathString = orig.codePathString; + setCodePath(orig.getCodePath()); cpuAbiOverrideString = orig.cpuAbiOverrideString; firstInstallTime = orig.firstInstallTime; installPermissionsFixed = orig.installPermissionsFixed; @@ -246,8 +240,6 @@ public abstract class PackageSettingBase extends SettingBase { legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString; // Intentionally skip mOldCodePaths; it's not relevant for copies primaryCpuAbiString = orig.primaryCpuAbiString; - resourcePath = orig.resourcePath; - resourcePathString = orig.resourcePathString; secondaryCpuAbiString = orig.secondaryCpuAbiString; signatures = orig.signatures; timeStamp = orig.timeStamp; @@ -705,6 +697,20 @@ public abstract class PackageSettingBase extends SettingBase { return userState.harmfulAppWarning; } + PackageSettingBase setCodePath(@NonNull File codePath) { + this.mCodePath = codePath; + this.mCodePathString = codePath.toString(); + return this; + } + + File getCodePath() { + return mCodePath; + } + + String getCodePathString() { + return mCodePathString; + } + /** * @see PackageUserState#overrideLabelAndIcon(ComponentName, String, Integer) * @@ -727,10 +733,7 @@ public abstract class PackageSettingBase extends SettingBase { protected PackageSettingBase updateFrom(PackageSettingBase other) { super.copyFrom(other); - this.codePath = other.codePath; - this.codePathString = other.codePathString; - this.resourcePath = other.resourcePath; - this.resourcePathString = other.resourcePathString; + setCodePath(other.getCodePath()); this.usesStaticLibraries = other.usesStaticLibraries; this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions; this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 1805713387ae..3e3e3c590491 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -538,7 +538,7 @@ public final class Settings { return null; } p.getPkgState().setUpdatedSystemApp(false); - PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath, + PackageSetting ret = addPackageLPw(name, p.realName, p.getCodePath(), p.legacyNativeLibraryPathString, p.primaryCpuAbiString, p.secondaryCpuAbiString, p.cpuAbiOverrideString, p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags, @@ -558,7 +558,7 @@ public final class Settings { mDisabledSysPackages.remove(name); } - PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath, + PackageSetting addPackageLPw(String name, String realName, File codePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries, @@ -572,10 +572,9 @@ public final class Settings { "Adding duplicate package, keeping first: " + name); return null; } - p = new PackageSetting(name, realName, codePath, resourcePath, - legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, - cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, - 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames, + p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString, + primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags, + pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames, mimeGroups); p.appId = uid; if (registerExistingAppIdLPw(uid, p, name)) { @@ -635,7 +634,7 @@ public final class Settings { */ static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg, PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser, - File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi, + File codePath, String legacyNativeLibraryPath, String primaryCpuAbi, String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags, UserHandle installUser, boolean allowInstall, boolean instantApp, boolean virtualPreload, UserManagerService userManager, @@ -646,12 +645,11 @@ public final class Settings { if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " + pkgName + " is adopting original package " + originalPkg.name); pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/); - pkgSetting.codePath = codePath; + pkgSetting.setCodePath(codePath); pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath; pkgSetting.pkgFlags = pkgFlags; pkgSetting.pkgPrivateFlags = pkgPrivateFlags; pkgSetting.primaryCpuAbiString = primaryCpuAbi; - pkgSetting.resourcePath = resourcePath; pkgSetting.secondaryCpuAbiString = secondaryCpuAbi; // NOTE: Create a deeper copy of the package signatures so we don't // overwrite the signatures in the original package setting. @@ -662,7 +660,7 @@ public final class Settings { // Update new package state. pkgSetting.setTimeStamp(codePath.lastModified()); } else { - pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath, + pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi, null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, usesStaticLibraries, @@ -756,10 +754,9 @@ public final class Settings { */ static void updatePackageSetting(@NonNull PackageSetting pkgSetting, @Nullable PackageSetting disabledPkg, @Nullable SharedUserSetting sharedUser, - @NonNull File codePath, File resourcePath, - @Nullable String legacyNativeLibraryPath, @Nullable String primaryCpuAbi, - @Nullable String secondaryCpuAbi, int pkgFlags, int pkgPrivateFlags, - @NonNull UserManagerService userManager, + @NonNull File codePath, @Nullable String legacyNativeLibraryPath, + @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags, + int pkgPrivateFlags, @NonNull UserManagerService userManager, @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions, @Nullable Set<String> mimeGroupNames) throws PackageManagerException { @@ -773,12 +770,12 @@ public final class Settings { "Updating application package " + pkgName + " failed"); } - if (!pkgSetting.codePath.equals(codePath)) { + if (!pkgSetting.getCodePath().equals(codePath)) { final boolean isSystem = pkgSetting.isSystem(); Slog.i(PackageManagerService.TAG, "Update" + (isSystem ? " system" : "") + " package " + pkgName - + " code path from " + pkgSetting.codePathString + + " code path from " + pkgSetting.getCodePathString() + " to " + codePath.toString() + "; Retain data and using new"); if (!isSystem) { @@ -800,19 +797,7 @@ public final class Settings { // internal to external storage or vice versa. pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath; } - pkgSetting.codePath = codePath; - pkgSetting.codePathString = codePath.toString(); - } - if (!pkgSetting.resourcePath.equals(resourcePath)) { - final boolean isSystem = pkgSetting.isSystem(); - Slog.i(PackageManagerService.TAG, - "Update" + (isSystem ? " system" : "") - + " package " + pkgName - + " resource path from " + pkgSetting.resourcePathString - + " to " + resourcePath.toString() - + "; Retain data and using new"); - pkgSetting.resourcePath = resourcePath; - pkgSetting.resourcePathString = resourcePath.toString(); + pkgSetting.setCodePath(codePath); } // If what we are scanning is a system (and possibly privileged) package, // then make it so, regardless of whether it was previously installed only @@ -2710,7 +2695,7 @@ public final class Settings { private void writePackageListLPrInternal(int creatingUserId) { // Only derive GIDs for active users (not dying) - final List<UserInfo> users = getUsers(UserManagerService.getInstance(), true); + final List<UserInfo> users = getActiveUsers(UserManagerService.getInstance(), true); int[] userIds = new int[users.size()]; for (int i = 0; i < userIds.length; i++) { userIds[i] = users.get(i).id; @@ -2812,14 +2797,11 @@ public final class Settings { if (pkg.realName != null) { serializer.attribute(null, "realName", pkg.realName); } - serializer.attribute(null, "codePath", pkg.codePathString); + serializer.attribute(null, "codePath", pkg.getCodePathString()); serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp)); serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime)); serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime)); serializer.attribute(null, "version", String.valueOf(pkg.versionCode)); - if (!pkg.resourcePathString.equals(pkg.codePathString)) { - serializer.attribute(null, "resourcePath", pkg.resourcePathString); - } if (pkg.legacyNativeLibraryPathString != null) { serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString); } @@ -2857,10 +2839,7 @@ public final class Settings { if (pkg.realName != null) { serializer.attribute(null, "realName", pkg.realName); } - serializer.attribute(null, "codePath", pkg.codePathString); - if (!pkg.resourcePathString.equals(pkg.codePathString)) { - serializer.attribute(null, "resourcePath", pkg.resourcePathString); - } + serializer.attribute(null, "codePath", pkg.getCodePathString()); if (pkg.legacyNativeLibraryPathString != null) { serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString); @@ -3559,13 +3538,10 @@ public final class Settings { String name = parser.getAttributeValue(null, ATTR_NAME); String realName = parser.getAttributeValue(null, "realName"); String codePathStr = parser.getAttributeValue(null, "codePath"); - String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); String legacyCpuAbiStr = parser.getAttributeValue(null, "requiredCpuAbi"); String legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); - String parentPackageName = parser.getAttributeValue(null, "parentPackageName"); - String primaryCpuAbiStr = parser.getAttributeValue(null, "primaryCpuAbi"); String secondaryCpuAbiStr = parser.getAttributeValue(null, "secondaryCpuAbi"); String cpuAbiOverrideStr = parser.getAttributeValue(null, "cpuAbiOverride"); @@ -3574,9 +3550,6 @@ public final class Settings { primaryCpuAbiStr = legacyCpuAbiStr; } - if (resourcePathStr == null) { - resourcePathStr = codePathStr; - } String version = parser.getAttributeValue(null, "version"); long versionCode = 0; if (version != null) { @@ -3593,9 +3566,8 @@ public final class Settings { pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; } PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr), - new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr, - secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags, - 0 /*sharedUserId*/, null, null, null); + legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr, + versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null); String timeStampStr = parser.getAttributeValue(null, "ft"); if (timeStampStr != null) { try { @@ -3666,7 +3638,6 @@ public final class Settings { String idStr = null; String sharedIdStr = null; String codePathStr = null; - String resourcePathStr = null; String legacyCpuAbiString = null; String legacyNativeLibraryPathStr = null; String primaryCpuAbiString = null; @@ -3700,7 +3671,6 @@ public final class Settings { uidError = parser.getAttributeValue(null, "uidError"); sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); codePathStr = parser.getAttributeValue(null, "codePath"); - resourcePathStr = parser.getAttributeValue(null, "resourcePath"); legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); @@ -3818,9 +3788,6 @@ public final class Settings { + " sharedUserId=" + sharedIdStr); final int userId = idStr != null ? Integer.parseInt(idStr) : 0; final int sharedUserId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; - if (resourcePathStr == null) { - resourcePathStr = codePathStr; - } if (realName != null) { realName = realName.intern(); } @@ -3834,10 +3801,10 @@ public final class Settings { + parser.getPositionDescription()); } else if (userId > 0) { packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr), - new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString, - secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags, - pkgPrivateFlags, null /*usesStaticLibraries*/, - null /*usesStaticLibraryVersions*/, null /*mimeGroups*/); + legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString, + cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags, + null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/, + null /*mimeGroups*/); if (PackageManagerService.DEBUG_SETTINGS) Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId=" + userId + " pkg=" + packageSetting); @@ -3852,8 +3819,8 @@ public final class Settings { } } else if (sharedIdStr != null) { if (sharedUserId > 0) { - packageSetting = new PackageSetting(name.intern(), realName, new File( - codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr, + packageSetting = new PackageSetting(name.intern(), realName, + new File(codePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, versionCode, pkgFlags, pkgPrivateFlags, sharedUserId, null /*usesStaticLibraries*/, @@ -4456,25 +4423,43 @@ public final class Settings { } /** - * Return all users on the device, including partial or dying users. + * Returns all users on the device, including pre-created and dying users. + * * @param userManager UserManagerService instance * @return the list of users */ private static List<UserInfo> getAllUsers(UserManagerService userManager) { - return getUsers(userManager, false); + return getUsers(userManager, /* excludeDying= */ false, /* excludePreCreated= */ false); } /** - * Return the list of users on the device. Clear the calling identity before calling into - * UserManagerService. + * Returns the list of users on the device, excluding pre-created ones. + * * @param userManager UserManagerService instance * @param excludeDying Indicates whether to exclude any users marked for deletion. + * + * @return the list of users + */ + private static List<UserInfo> getActiveUsers(UserManagerService userManager, + boolean excludeDying) { + return getUsers(userManager, excludeDying, /* excludePreCreated= */ true); + } + + /** + * Returns the list of users on the device. + * + * @param userManager UserManagerService instance + * @param excludeDying Indicates whether to exclude any users marked for deletion. + * @param excludePreCreated Indicates whether to exclude any pre-created users. + * * @return the list of users */ - private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying) { + private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying, + boolean excludePreCreated) { long id = Binder.clearCallingIdentity(); try { - return userManager.getUsers(excludeDying); + return userManager.getUsers(/* excludePartial= */ true, excludeDying, + excludePreCreated); } catch (NullPointerException npe) { // packagemanager not yet initialized } finally { @@ -4657,9 +4642,9 @@ public final class Settings { pw.print(prefix); pw.print(" sharedUser="); pw.println(ps.sharedUser); } pw.print(prefix); pw.print(" pkg="); pw.println(pkg); - pw.print(prefix); pw.print(" codePath="); pw.println(ps.codePathString); + pw.print(prefix); pw.print(" codePath="); pw.println(ps.getCodePathString()); if (permissionNames == null) { - pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.resourcePathString); + pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.getCodePathString()); pw.print(prefix); pw.print(" legacyNativeLibraryDir="); pw.println(ps.legacyNativeLibraryPathString); pw.print(prefix); pw.print(" primaryCpuAbi="); pw.println(ps.primaryCpuAbiString); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 89ed3c755783..700f7be83e15 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -260,7 +260,8 @@ public class ShortcutService extends IShortcutService.Stub { private static final int PACKAGE_MATCH_FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_UNINSTALLED_PACKAGES; + | PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS; private static final int SYSTEM_APP_MASK = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; @@ -278,12 +279,6 @@ public class ShortcutService extends IShortcutService.Stub { } }; - private static Predicate<ResolveInfo> ACTIVITY_NOT_SYSTEM_NOR_ENABLED = (ri) -> { - final ApplicationInfo ai = ri.activityInfo.applicationInfo; - final boolean isSystemApp = ai != null && (ai.flags & SYSTEM_APP_MASK) != 0; - return !isSystemApp && !ri.activityInfo.enabled; - }; - private static Predicate<ResolveInfo> ACTIVITY_NOT_INSTALLED = (ri) -> !isInstalled(ri.activityInfo); @@ -3685,10 +3680,8 @@ public class ShortcutService extends IShortcutService.Stub { final long start = getStatStartTime(); final long token = injectClearCallingIdentity(); try { - return mIPackageManager.getPackageInfo( - packageName, PACKAGE_MATCH_FLAGS | PackageManager.MATCH_DISABLED_COMPONENTS - | (getSignatures ? PackageManager.GET_SIGNING_CERTIFICATES : 0), - userId); + return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS + | (getSignatures ? PackageManager.GET_SIGNING_CERTIFICATES : 0), userId); } catch (RemoteException e) { // Shouldn't happen. Slog.wtf(TAG, "RemoteException", e); @@ -3721,8 +3714,7 @@ public class ShortcutService extends IShortcutService.Stub { final long start = getStatStartTime(); final long token = injectClearCallingIdentity(); try { - return mIPackageManager.getApplicationInfo(packageName, - PACKAGE_MATCH_FLAGS | PackageManager.MATCH_DISABLED_COMPONENTS, userId); + return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId); } catch (RemoteException e) { // Shouldn't happen. Slog.wtf(TAG, "RemoteException", e); @@ -3753,9 +3745,8 @@ public class ShortcutService extends IShortcutService.Stub { final long start = getStatStartTime(); final long token = injectClearCallingIdentity(); try { - return mIPackageManager.getActivityInfo(activity, (PACKAGE_MATCH_FLAGS - | PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_META_DATA), - userId); + return mIPackageManager.getActivityInfo(activity, + PACKAGE_MATCH_FLAGS | PackageManager.GET_META_DATA, userId); } catch (RemoteException e) { // Shouldn't happen. Slog.wtf(TAG, "RemoteException", e); @@ -3800,8 +3791,7 @@ public class ShortcutService extends IShortcutService.Stub { List<PackageInfo> injectGetPackagesWithUninstalled(@UserIdInt int userId) throws RemoteException { final ParceledListSlice<PackageInfo> parceledList = - mIPackageManager.getInstalledPackages( - PACKAGE_MATCH_FLAGS | PackageManager.MATCH_DISABLED_COMPONENTS, userId); + mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId); if (parceledList == null) { return Collections.emptyList(); } @@ -3836,6 +3826,41 @@ public class ShortcutService extends IShortcutService.Stub { return (ai != null) && ((ai.flags & flags) == flags); } + // Due to b/38267327, ActivityInfo.enabled may not reflect the current state of the component + // and we need to check the enabled state via PackageManager.getComponentEnabledSetting. + private boolean isEnabled(@Nullable ActivityInfo ai, int userId) { + if (ai == null) { + return false; + } + + int enabledFlag; + final long token = injectClearCallingIdentity(); + try { + enabledFlag = mIPackageManager.getComponentEnabledSetting( + ai.getComponentName(), userId); + } catch (RemoteException e) { + // Shouldn't happen. + Slog.wtf(TAG, "RemoteException", e); + return false; + } finally { + injectRestoreCallingIdentity(token); + } + + if ((enabledFlag == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && ai.enabled) + || enabledFlag == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { + return true; + } + return false; + } + + private static boolean isSystem(@Nullable ActivityInfo ai) { + return (ai != null) && isSystem(ai.applicationInfo); + } + + private static boolean isSystem(@Nullable ApplicationInfo ai) { + return (ai != null) && (ai.flags & SYSTEM_APP_MASK) != 0; + } + private static boolean isInstalled(@Nullable ApplicationInfo ai) { return (ai != null) && ai.enabled && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0; } @@ -3900,12 +3925,6 @@ public class ShortcutService extends IShortcutService.Stub { return intent; } - private static boolean isSystemApp(@Nullable final ApplicationInfo ai) { - final int systemAppMask = - ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - return ai != null && ((ai.flags & systemAppMask) != 0); - } - /** * Same as queryIntentActivitiesAsUser, except it makes sure the package is installed, * and only returns exported activities. @@ -3938,7 +3957,10 @@ public class ShortcutService extends IShortcutService.Stub { } // Make sure the package is installed. resolved.removeIf(ACTIVITY_NOT_INSTALLED); - resolved.removeIf(ACTIVITY_NOT_SYSTEM_NOR_ENABLED); + resolved.removeIf((ri) -> { + final ActivityInfo ai = ri.activityInfo; + return !isSystem(ai) && !isEnabled(ai, userId); + }); if (exportedOnly) { resolved.removeIf(ACTIVITY_NOT_EXPORTED); } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 89bdb3ecbff9..f9bf54a11df0 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -322,9 +322,6 @@ public class StagingManager { } final long activeVersion = activePackage.applicationInfo.longVersionCode; if (activeVersion != session.params.requiredInstalledVersionCode) { - if (!mApexManager.abortStagedSession(session.sessionId)) { - Slog.e(TAG, "Failed to abort apex session " + session.sessionId); - } throw new PackageManagerException( SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "Installed version of APEX package " + activePackage.packageName @@ -338,14 +335,11 @@ public class StagingManager { throws PackageManagerException { final long activeVersion = activePackage.applicationInfo.longVersionCode; final long newVersionCode = newPackage.applicationInfo.longVersionCode; - boolean isAppDebuggable = (activePackage.applicationInfo.flags + final boolean isAppDebuggable = (activePackage.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted( session.params.installFlags, isAppDebuggable); if (activeVersion > newVersionCode && !allowsDowngrade) { - if (!mApexManager.abortStagedSession(session.sessionId)) { - Slog.e(TAG, "Failed to abort apex session " + session.sessionId); - } throw new PackageManagerException( SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "Downgrade of APEX package " + newPackage.packageName @@ -835,37 +829,6 @@ public class StagingManager { return null; } - private void verifyApksInSession(PackageInstallerSession session) - throws PackageManagerException { - - final PackageInstallerSession apksToVerify = extractApksInSession( - session, /* preReboot */ true); - if (apksToVerify == null) { - return; - } - - final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync( - (Intent result) -> { - int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE); - if (status != PackageInstaller.STATUS_SUCCESS) { - final String errorMessage = result.getStringExtra( - PackageInstaller.EXTRA_STATUS_MESSAGE); - Slog.e(TAG, "Failure to verify APK staged session " - + session.sessionId + " [" + errorMessage + "]"); - session.setStagedSessionFailed( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage); - mPreRebootVerificationHandler.onPreRebootVerificationComplete( - session.sessionId); - return; - } - mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( - session.sessionId); - }); - - apksToVerify.commit(receiver.getIntentSender(), false); - } - private void installApksInSession(@NonNull PackageInstallerSession session) throws PackageManagerException { @@ -908,10 +871,21 @@ public class StagingManager { mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId); } - private int parentOrOwnSessionId(PackageInstallerSession session) { + private int getSessionIdForParentOrSelf(PackageInstallerSession session) { return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId; } + private PackageInstallerSession getParentSessionOrSelf(PackageInstallerSession session) { + return session.hasParentSessionId() + ? getStagedSession(session.getParentSessionId()) + : session; + } + + private boolean isRollback(PackageInstallerSession session) { + final PackageInstallerSession root = getParentSessionOrSelf(session); + return root.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK; + } + /** * <p> Check if the session provided is non-overlapping with the active staged sessions. * @@ -937,6 +911,8 @@ public class StagingManager { boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService( Context.STORAGE_SERVICE)).isCheckpointSupported(); + final boolean isRollback = isRollback(session); + synchronized (mStagedSessions) { for (int i = 0; i < mStagedSessions.size(); i++) { final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i); @@ -951,8 +927,8 @@ public class StagingManager { } // Check if stagedSession has an active parent session or not if (stagedSession.hasParentSessionId()) { - int parentId = stagedSession.getParentSessionId(); - PackageInstallerSession parentSession = mStagedSessions.get(parentId); + final int parentId = stagedSession.getParentSessionId(); + final PackageInstallerSession parentSession = mStagedSessions.get(parentId); if (parentSession == null || parentSession.isStagedAndInTerminalState() || parentSession.isDestroyed()) { // Parent session has been abandoned or terminated already @@ -968,21 +944,37 @@ public class StagingManager { continue; } - // If session is not among the active sessions, then it cannot have same package - // name as any of the active sessions. + // New session cannot have same package name as one of the active sessions if (session.getPackageName().equals(stagedSession.getPackageName())) { - throw new PackageManagerException( - PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS, - "Package: " + session.getPackageName() + " in session: " - + session.sessionId + " has been staged already by session: " - + stagedSession.sessionId, null); + if (isRollback) { + // If the new session is a rollback, then it gets priority. The existing + // session is failed to unblock rollback. + final PackageInstallerSession root = getParentSessionOrSelf(stagedSession); + if (!ensureActiveApexSessionIsAborted(root)) { + Slog.e(TAG, "Failed to abort apex session " + root.sessionId); + // Safe to ignore active apex session abort failure since session + // will be marked failed on next step and staging directory for session + // will be deleted. + } + root.setStagedSessionFailed( + SessionInfo.STAGED_SESSION_OTHER_ERROR, + "Session was blocking rollback session: " + session.sessionId); + Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to " + + "blocking rollback session: " + session.sessionId); + } else { + throw new PackageManagerException( + PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS, + "Package: " + session.getPackageName() + " in session: " + + session.sessionId + " has been staged already by session:" + + " " + stagedSession.sessionId, null); + } } // Staging multiple root sessions is not allowed if device doesn't support // checkpoint. If session and stagedSession do not have common ancestor, they are // from two different root sessions. - if (!supportsCheckpoint - && parentOrOwnSessionId(session) != parentOrOwnSessionId(stagedSession)) { + if (!supportsCheckpoint && getSessionIdForParentOrSelf(session) + != getSessionIdForParentOrSelf(stagedSession)) { throw new PackageManagerException( PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS, "Cannot stage multiple sessions without checkpoint support", null); @@ -1042,23 +1034,11 @@ public class StagingManager { // A session could be marked ready once its pre-reboot verification ends if (session.isStagedSessionReady()) { - if (sessionContainsApex(session)) { - try { - ApexSessionInfo apexSession = - mApexManager.getStagedSessionInfo(session.sessionId); - if (apexSession == null || isApexSessionFinalized(apexSession)) { - Slog.w(TAG, - "Cannot abort session " + session.sessionId - + " because it is not active."); - } else { - mApexManager.abortStagedSession(session.sessionId); - } - } catch (Exception e) { - // Failed to contact apexd service. The apex might still be staged. We can still - // safely cleanup the staged session since pre-reboot verification is complete. - // Also, cleaning up the stageDir prevents the apex from being activated. - Slog.w(TAG, "Could not contact apexd to abort staged session " + sessionId); - } + if (!ensureActiveApexSessionIsAborted(session)) { + // Failed to ensure apex session is aborted, so it can still be staged. We can still + // safely cleanup the staged session since pre-reboot verification is complete. + // Also, cleaning up the stageDir prevents the apex from being activated. + Slog.e(TAG, "Failed to abort apex session " + session.sessionId); } } @@ -1068,6 +1048,22 @@ public class StagingManager { return true; } + /** + * Ensure that there is no active apex session staged in apexd for the given session. + * + * @return returns true if it is ensured that there is no active apex session, otherwise false + */ + private boolean ensureActiveApexSessionIsAborted(PackageInstallerSession session) { + if (!sessionContainsApex(session)) { + return true; + } + final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId); + if (apexSession == null || isApexSessionFinalized(apexSession)) { + return true; + } + return mApexManager.abortStagedSession(session.sessionId); + } + private boolean isApexSessionFinalized(ApexSessionInfo session) { /* checking if the session is in a final state, i.e., not active anymore */ return session.isUnknown || session.isActivationFailed || session.isSuccess @@ -1294,8 +1290,8 @@ public class StagingManager { + sessionId); return; } - if (session.isDestroyed()) { - // No point in running verification on a destroyed session + if (session.isDestroyed() || session.isStagedSessionFailed()) { + // No point in running verification on a destroyed/failed session onPreRebootVerificationComplete(sessionId); return; } @@ -1348,6 +1344,17 @@ public class StagingManager { obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget(); } + private void onPreRebootVerificationFailure(PackageInstallerSession session, + @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) { + if (!ensureActiveApexSessionIsAborted(session)) { + Slog.e(TAG, "Failed to abort apex session " + session.sessionId); + // Safe to ignore active apex session abortion failure since session will be marked + // failed on next step and staging directory for session will be deleted. + } + session.setStagedSessionFailed(errorCode, errorMessage); + onPreRebootVerificationComplete(session.sessionId); + } + // Things to do when pre-reboot verification completes for a particular sessionId private void onPreRebootVerificationComplete(int sessionId) { // Remove it from mVerificationRunning so that verification is considered complete @@ -1432,8 +1439,7 @@ public class StagingManager { validateApexSignature(apexPackages.get(i)); } } catch (PackageManagerException e) { - session.setStagedSessionFailed(e.error, e.getMessage()); - onPreRebootVerificationComplete(session.sessionId); + onPreRebootVerificationFailure(session, e.error, e.getMessage()); return; } @@ -1460,16 +1466,42 @@ public class StagingManager { try { Slog.d(TAG, "Running a pre-reboot verification for APKs in session " + session.sessionId + " by performing a dry-run install"); - // verifyApksInSession will notify the handler when APK verification is complete verifyApksInSession(session); - // TODO(b/118865310): abort the session on apexd. } catch (PackageManagerException e) { - session.setStagedSessionFailed(e.error, e.getMessage()); - onPreRebootVerificationComplete(session.sessionId); + onPreRebootVerificationFailure(session, e.error, e.getMessage()); } } + private void verifyApksInSession(PackageInstallerSession session) + throws PackageManagerException { + + final PackageInstallerSession apksToVerify = extractApksInSession( + session, /* preReboot */ true); + if (apksToVerify == null) { + return; + } + + final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync( + (Intent result) -> { + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status != PackageInstaller.STATUS_SUCCESS) { + final String errorMessage = result.getStringExtra( + PackageInstaller.EXTRA_STATUS_MESSAGE); + Slog.e(TAG, "Failure to verify APK staged session " + + session.sessionId + " [" + errorMessage + "]"); + onPreRebootVerificationFailure(session, + SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage); + return; + } + mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( + session.sessionId); + }); + + apksToVerify.commit(receiver.getIntentSender(), false); + } + /** * Pre-reboot verification state for wrapping up: * <p><ul> @@ -1487,9 +1519,8 @@ public class StagingManager { } catch (Exception e) { // Failed to get hold of StorageManager Slog.e(TAG, "Failed to get hold of StorageManager", e); - session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, + onPreRebootVerificationFailure(session, SessionInfo.STAGED_SESSION_UNKNOWN, "Failed to get hold of StorageManager"); - onPreRebootVerificationComplete(session.sessionId); return; } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 8f11fd529e60..e3bee7228c68 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3497,7 +3497,7 @@ public class UserManagerService extends IUserManager.Stub { } t.traceBegin("PM.onNewUserCreated-" + userId); - mPm.onNewUserCreated(userId); + mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false); t.traceEnd(); if (preCreate) { // Must start user (which will be stopped right away, through @@ -3570,10 +3570,7 @@ public class UserManagerService extends IUserManager.Stub { writeUserListLP(); } updateUserIds(); - if (!mPm.readPermissionStateForUser(preCreatedUser.id)) { - // Could not read the existing permissions, re-grant them. - mPm.onNewUserCreated(preCreatedUser.id); - } + mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true); dispatchUserAdded(preCreatedUser); return preCreatedUser; } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index 3ec61fdda917..7467439905eb 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -18,7 +18,6 @@ package com.android.server.timezonedetector; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.app.timezonedetector.ITimeZoneConfigurationListener; import android.app.timezonedetector.ITimeZoneDetectorService; import android.app.timezonedetector.ManualTimeZoneSuggestion; @@ -38,6 +37,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.IndentingPrintWriter; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -60,7 +60,8 @@ import java.util.Objects; * and making calls async, leaving the (consequently more testable) {@link TimeZoneDetectorStrategy} * implementation to deal with the logic around time zone detection. */ -public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub { +public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub + implements IBinder.DeathRecipient { private static final String TAG = "TimeZoneDetectorService"; @@ -104,9 +105,15 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy; + /** + * This sparse array acts as a map from userId to listeners running as that userId. User scoped + * as time zone detection configuration is partially user-specific, so different users can + * get different configuration. + */ @GuardedBy("mConfigurationListeners") @NonNull - private final ArrayList<ConfigListenerInfo> mConfigurationListeners = new ArrayList<>(); + private final SparseArray<ArrayList<ITimeZoneConfigurationListener>> mConfigurationListeners = + new SparseArray<>(); private static TimeZoneDetectorService create( @NonNull Context context, @NonNull Handler handler, @@ -188,18 +195,23 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub Objects.requireNonNull(listener); int userId = UserHandle.getCallingUserId(); - ConfigListenerInfo listenerInfo = new ConfigListenerInfo(userId, listener); - synchronized (mConfigurationListeners) { - if (mConfigurationListeners.contains(listenerInfo)) { + ArrayList<ITimeZoneConfigurationListener> listeners = + mConfigurationListeners.get(userId); + if (listeners != null && listeners.contains(listener)) { return; } try { - // Ensure the reference to the listener is removed if the client process dies. - listenerInfo.linkToDeath(); + if (listeners == null) { + listeners = new ArrayList<>(1); + mConfigurationListeners.put(userId, listeners); + } + + // Ensure the reference to the listener will be removed if the client process dies. + listener.asBinder().linkToDeath(this, 0 /* flags */); // Only add the listener if we can linkToDeath(). - mConfigurationListeners.add(listenerInfo); + listeners.add(listener); } catch (RemoteException e) { Slog.e(TAG, "Unable to linkToDeath() for listener=" + listener, e); } @@ -213,21 +225,56 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub int userId = UserHandle.getCallingUserId(); synchronized (mConfigurationListeners) { - ConfigListenerInfo toRemove = new ConfigListenerInfo(userId, listener); - Iterator<ConfigListenerInfo> listenerIterator = mConfigurationListeners.iterator(); - while (listenerIterator.hasNext()) { - ConfigListenerInfo currentListenerInfo = listenerIterator.next(); - if (currentListenerInfo.equals(toRemove)) { - listenerIterator.remove(); - - // Stop listening for the client process to die. - try { - currentListenerInfo.unlinkToDeath(); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to unlinkToDeath() for listener=" + listener, e); + boolean removedListener = false; + ArrayList<ITimeZoneConfigurationListener> userListeners = + mConfigurationListeners.get(userId); + if (userListeners.remove(listener)) { + // Stop listening for the client process to die. + listener.asBinder().unlinkToDeath(this, 0 /* flags */); + removedListener = true; + } + if (!removedListener) { + Slog.w(TAG, "Client asked to remove listenener=" + listener + + ", but no listeners were removed." + + " mConfigurationListeners=" + mConfigurationListeners); + } + } + } + + @Override + public void binderDied() { + // Should not be used as binderDied(IBinder who) is overridden. + Slog.wtf(TAG, "binderDied() called unexpectedly."); + } + + /** + * Called when one of the ITimeZoneConfigurationListener processes dies before calling + * {@link #removeConfigurationListener(ITimeZoneConfigurationListener)}. + */ + @Override + public void binderDied(IBinder who) { + synchronized (mConfigurationListeners) { + boolean removedListener = false; + final int userCount = mConfigurationListeners.size(); + for (int i = 0; i < userCount; i++) { + ArrayList<ITimeZoneConfigurationListener> userListeners = + mConfigurationListeners.valueAt(i); + Iterator<ITimeZoneConfigurationListener> userListenerIterator = + userListeners.iterator(); + while (userListenerIterator.hasNext()) { + ITimeZoneConfigurationListener userListener = userListenerIterator.next(); + if (userListener.asBinder().equals(who)) { + userListenerIterator.remove(); + removedListener = true; + break; } } } + if (!removedListener) { + Slog.w(TAG, "Notified of binder death for who=" + who + + ", but did not remove any listeners." + + " mConfigurationListeners=" + mConfigurationListeners); + } } } @@ -243,14 +290,24 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub // problem. synchronized (mConfigurationListeners) { - for (ConfigListenerInfo listenerInfo : mConfigurationListeners) { + final int userCount = mConfigurationListeners.size(); + for (int userIndex = 0; userIndex < userCount; userIndex++) { + int userId = mConfigurationListeners.keyAt(userIndex); TimeZoneConfiguration configuration = - mTimeZoneDetectorStrategy.getConfiguration(listenerInfo.getUserId()); - try { - listenerInfo.getListener().onChange(configuration); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to notify listener=" - + listenerInfo + " of updated configuration=" + configuration, e); + mTimeZoneDetectorStrategy.getConfiguration(userId); + + ArrayList<ITimeZoneConfigurationListener> listeners = + mConfigurationListeners.valueAt(userIndex); + final int listenerCount = listeners.size(); + for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) { + ITimeZoneConfigurationListener listener = listeners.get(listenerIndex); + try { + listener.onChange(configuration); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to notify listener=" + listener + + " for userId=" + userId + + " of updated configuration=" + configuration, e); + } } } } @@ -338,66 +395,5 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub (new TimeZoneDetectorShellCommand(this)).exec( this, in, out, err, args, callback, resultReceiver); } - - private class ConfigListenerInfo implements IBinder.DeathRecipient { - private final @UserIdInt int mUserId; - private final ITimeZoneConfigurationListener mListener; - - ConfigListenerInfo( - @UserIdInt int userId, @NonNull ITimeZoneConfigurationListener listener) { - this.mUserId = userId; - this.mListener = Objects.requireNonNull(listener); - } - - @UserIdInt int getUserId() { - return mUserId; - } - - ITimeZoneConfigurationListener getListener() { - return mListener; - } - - void linkToDeath() throws RemoteException { - mListener.asBinder().linkToDeath(this, 0 /* flags */); - } - - void unlinkToDeath() throws RemoteException { - mListener.asBinder().unlinkToDeath(this, 0 /* flags */); - } - - @Override - public void binderDied() { - synchronized (mConfigurationListeners) { - Slog.i(TAG, "Configuration listener client died: " + this); - mConfigurationListeners.remove(this); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ConfigListenerInfo that = (ConfigListenerInfo) o; - return mUserId == that.mUserId - && mListener.equals(that.mListener); - } - - @Override - public int hashCode() { - return Objects.hash(mUserId, mListener); - } - - @Override - public String toString() { - return "ConfigListenerInfo{" - + "mUserId=" + mUserId - + ", mListener=" + mListener - + '}'; - } - } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b72ff2db7e6c..1b4fac6f407a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -70,7 +70,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS; import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY; @@ -168,7 +168,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; -import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked; +import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked; import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; import static com.android.server.wm.IdentifierProto.USER_ID; @@ -587,8 +587,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private boolean mOccludesParent; - // The input dispatching timeout for this application token in nanoseconds. - long mInputDispatchingTimeoutNanos; + // The input dispatching timeout for this application token in milliseconds. + long mInputDispatchingTimeoutMillis; private boolean mShowWhenLocked; private boolean mInheritShownWhenLocked; @@ -1245,7 +1245,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (oldParent == null && newParent != null) { // First time we are adding the activity to the system. mVoiceInteraction = newTask.voiceSession != null; - mInputDispatchingTimeoutNanos = getInputDispatchingTimeoutLocked(this) * 1000000L; + mInputDispatchingTimeoutMillis = getInputDispatchingTimeoutMillisLocked(this); // TODO(b/36505427): Maybe this call should be moved inside // updateOverrideConfiguration() @@ -1683,7 +1683,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (options != null) { final boolean useLockTask = options.getLockTaskMode(); if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) { - lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; + lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; } } return lockTaskLaunchMode; @@ -5651,8 +5651,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { // In this case another process added windows using this activity token. So, we call the // generic service input dispatch timed out method so that the right process is blamed. - return mAtmService.mAmInternal.inputDispatchingTimedOut( - windowPid, false /* aboveSystem */, reason) < 0; + long timeoutMillis = mAtmService.mAmInternal.inputDispatchingTimedOut( + windowPid, false /* aboveSystem */, reason); + return timeoutMillis <= 0; } } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index ed1ea353eb3e..2c475e0b9bcb 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -74,9 +74,9 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.Task.ActivityState.PAUSED; import static com.android.server.wm.Task.ActivityState.PAUSING; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK; +import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; -import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED; import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.Task.TAG_CLEANUP; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; @@ -130,6 +130,7 @@ import android.util.MergedConfiguration; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; +import android.view.Display; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -788,7 +789,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final LockTaskController lockTaskController = mService.getLockTaskController(); if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV - || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED + || (task.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED && lockTaskController.getLockTaskModeState() == LOCK_TASK_MODE_LOCKED)) { lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */); @@ -1090,9 +1091,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Check if caller is already present on display final boolean uidPresentOnDisplay = displayContent.isUidPresent(callingUid); - final int displayOwnerUid = displayContent.mDisplay.getOwnerUid(); - if (displayContent.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID) { - // Limit launching on virtual displays, because their contents can be read from Surface + final Display display = displayContent.mDisplay; + if (!display.isTrusted()) { + // Limit launching on untrusted displays because their contents can be read from Surface // by apps that created them. if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) { if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:" @@ -1116,7 +1117,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } // Check if the caller is the owner of the display. - if (displayOwnerUid == callingUid) { + if (display.getOwnerUid() == callingUid) { if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:" + " allow launch for owner of the display"); return true; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 777ddda89e9d..2dc22ecfc022 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -528,8 +528,8 @@ public abstract class ActivityTaskManagerInternal { public abstract void onActiveUidsCleared(); public abstract void onUidProcStateChanged(int uid, int procState); - public abstract void onUidAddedToPendingTempWhitelist(int uid, String tag); - public abstract void onUidRemovedFromPendingTempWhitelist(int uid); + public abstract void onUidAddedToPendingTempAllowlist(int uid, String tag); + public abstract void onUidRemovedFromPendingTempAllowlist(int uid); /** Handle app crash event in {@link android.app.IActivityController} if there is one. */ public abstract boolean handleAppCrashInActivityController(String processName, int pid, diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 5534b8c257f0..627361d780a2 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -50,6 +50,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.FactoryTest.FACTORY_TEST_HIGH_LEVEL; import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL; import static android.os.FactoryTest.FACTORY_TEST_OFF; +import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.Process.FIRST_APPLICATION_UID; import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -182,7 +183,6 @@ import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; -import android.metrics.LogMaker; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -237,12 +237,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AssistUtils; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ProcessMap; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.TransferPipe; -import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.KeyguardDismissCallback; import com.android.internal.util.ArrayUtils; @@ -314,10 +311,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK; private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; - // How long we wait until we timeout on key dispatching. - public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000; // How long we wait until we timeout on key dispatching during instrumentation. - static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000; + static final long INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS = 60 * 1000; // How long we permit background activity starts after an activity in the process // started or finished. static final long ACTIVITY_BG_START_GRACE_PERIOD_MS = 10 * 1000; @@ -385,7 +380,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private AppOpsManager mAppOpsManager; /** All active uids in the system. */ private final MirrorActiveUids mActiveUids = new MirrorActiveUids(); - private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>(); + private final SparseArray<String> mPendingTempAllowlist = new SparseArray<>(); /** All processes currently running that might have a window organized by name. */ final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>(); /** All processes we currently have running mapped by pid and uid */ @@ -1111,7 +1106,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public int startActivityIntentSender(IApplicationThread caller, IIntentSender target, - IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo, + IBinder allowlistToken, Intent fillInIntent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) { enforceNotIsolatedCaller("startActivityIntentSender"); // Refuse possible leaked file descriptors @@ -1134,7 +1129,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mAppSwitchesAllowedTime = 0; } } - return pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null, + return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null, resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions); } @@ -3038,7 +3033,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // system or a specific app. // * System-initiated requests will only start the pinned mode (screen pinning) // * App-initiated requests - // - will put the device in fully locked mode (LockTask), if the app is whitelisted + // - will put the device in fully locked mode (LockTask), if the app is allowlisted // - will start the pinned mode, otherwise final int callingUid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); @@ -3078,7 +3073,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "updateLockTaskPackages()"); } synchronized (mGlobalLock) { - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" + if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowlisting " + userId + ":" + Arrays.toString(packages)); getLockTaskController().updateLockTaskPackages(userId, packages); } @@ -4096,10 +4091,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final Task stack = r.getRootTask(); stack.setPictureInPictureAspectRatio(aspectRatio); stack.setPictureInPictureActions(actions); - MetricsLoggerWrapper.logPictureInPictureEnter(mContext, - r.info.applicationInfo.uid, r.shortComponentName, - r.supportsEnterPipOnTaskSwitch); - logPictureInPictureArgs(params); } }; @@ -4143,7 +4134,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.pictureInPictureArgs.getAspectRatio()); stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); } - logPictureInPictureArgs(params); } } finally { Binder.restoreCallingIdentity(origId); @@ -4157,18 +4147,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return 3; } - private void logPictureInPictureArgs(PictureInPictureParams params) { - if (params.hasSetActions()) { - MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count", - params.getActions().size()); - } - if (params.hasSetAspectRatio()) { - LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED); - lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio()); - MetricsLogger.action(lm); - } - } - /** * Checks the state of the system and the activity associated with the given {@param token} to * verify that picture-in-picture is supported for that activity. @@ -5376,15 +5354,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - static long getInputDispatchingTimeoutLocked(ActivityRecord r) { + static long getInputDispatchingTimeoutMillisLocked(ActivityRecord r) { if (r == null || !r.hasProcess()) { - return KEY_DISPATCHING_TIMEOUT_MS; + return DEFAULT_DISPATCHING_TIMEOUT_MILLIS; } - return getInputDispatchingTimeoutLocked(r.app); + return getInputDispatchingTimeoutMillisLocked(r.app); } - private static long getInputDispatchingTimeoutLocked(WindowProcessController r) { - return r != null ? r.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS; + private static long getInputDispatchingTimeoutMillisLocked(WindowProcessController r) { + if (r == null) { + return DEFAULT_DISPATCHING_TIMEOUT_MILLIS; + } + return r.getInputDispatchingTimeoutMillis(); } /** @@ -5977,11 +5958,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } /** - * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on - * the whitelist + * @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on + * the allowlist */ - String getPendingTempWhitelistTagForUidLocked(int uid) { - return mPendingTempWhitelist.get(uid); + String getPendingTempAllowlistTagForUidLocked(int uid) { + return mPendingTempAllowlist.get(uid); } void logAppTooSlow(WindowProcessController app, long startTime, String msg) { @@ -7323,16 +7304,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void onUidAddedToPendingTempWhitelist(int uid, String tag) { + public void onUidAddedToPendingTempAllowlist(int uid, String tag) { synchronized (mGlobalLockWithoutBoost) { - mPendingTempWhitelist.put(uid, tag); + mPendingTempAllowlist.put(uid, tag); } } @Override - public void onUidRemovedFromPendingTempWhitelist(int uid) { + public void onUidRemovedFromPendingTempAllowlist(int uid) { synchronized (mGlobalLockWithoutBoost) { - mPendingTempWhitelist.remove(uid); + mPendingTempAllowlist.remove(uid); } } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index f840d9273f60..22dd1d332345 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; + import static com.android.server.wm.DragDropController.MSG_ANIMATION_END; import static com.android.server.wm.DragDropController.MSG_DRAG_END_TIMEOUT; import static com.android.server.wm.DragDropController.MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT; @@ -271,8 +273,7 @@ class DragState { mDragApplicationHandle = new InputApplicationHandle(new Binder()); mDragApplicationHandle.name = "drag"; - mDragApplicationHandle.dispatchingTimeoutNanos = - WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + mDragApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, display.getDisplayId()); @@ -280,8 +281,7 @@ class DragState { mDragWindowHandle.token = mServerChannel.getToken(); mDragWindowHandle.layoutParamsFlags = 0; mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; - mDragWindowHandle.dispatchingTimeoutNanos = - WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; mDragWindowHandle.visible = true; mDragWindowHandle.canReceiveKeys = false; mDragWindowHandle.hasFocus = true; diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java index 852b367259c1..3b24584b0357 100644 --- a/services/core/java/com/android/server/wm/InputConsumerImpl.java +++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; + import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; @@ -69,16 +71,14 @@ class InputConsumerImpl implements IBinder.DeathRecipient { mApplicationHandle = new InputApplicationHandle(new Binder()); mApplicationHandle.name = name; - mApplicationHandle.dispatchingTimeoutNanos = - WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + mApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId); mWindowHandle.name = name; mWindowHandle.token = mServerChannel.getToken(); mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; mWindowHandle.layoutParamsFlags = 0; - mWindowHandle.dispatchingTimeoutNanos = - WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; mWindowHandle.visible = true; mWindowHandle.canReceiveKeys = false; mWindowHandle.hasFocus = false; diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index 9c4ac890fed8..e166bfc08ad4 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -10,6 +10,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS; +import android.annotation.Nullable; import android.os.Build; import android.os.Debug; import android.os.IBinder; @@ -173,23 +174,23 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal */ @Override public long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, - String reason) { + @Nullable Integer pid, String reason) { final long startTime = SystemClock.uptimeMillis(); try { - return notifyANRInner(inputApplicationHandle, token, reason); + return notifyANRInner(inputApplicationHandle, token, pid, reason); } finally { // Log the time because the method is called from InputDispatcher thread. It shouldn't - // take too long that may affect input response time. + // take too long because it blocks input while executing. Slog.d(TAG_WM, "notifyANR took " + (SystemClock.uptimeMillis() - startTime) + "ms"); } } private long notifyANRInner(InputApplicationHandle inputApplicationHandle, IBinder token, - String reason) { + @Nullable Integer pid, String reason) { ActivityRecord activity = null; WindowState windowState = null; boolean aboveSystem = false; - int windowPid = INVALID_PID; + int windowPid = pid != null ? pid : INVALID_PID; preDumpIfLockTooSlow(); @@ -258,18 +259,14 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal if (!abort) { // The activity manager declined to abort dispatching. // Wait a bit longer and timeout again later. - return activity.mInputDispatchingTimeoutNanos; + return TimeUnit.MILLISECONDS.toNanos(activity.mInputDispatchingTimeoutMillis); } } else if (windowState != null || windowPid != INVALID_PID) { // Notify the activity manager about the timeout and let it decide whether // to abort dispatching or keep waiting. - long timeout = mService.mAmInternal.inputDispatchingTimedOut(windowPid, aboveSystem, - reason); - if (timeout >= 0) { - // The activity manager declined to abort dispatching. - // Wait a bit longer and timeout again later. - return timeout * 1000000L; // nanoseconds - } + long timeoutMillis = + mService.mAmInternal.inputDispatchingTimedOut(windowPid, aboveSystem, reason); + return TimeUnit.MILLISECONDS.toNanos(timeoutMillis); } return 0; // abort dispatching } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 20f1b9f53013..791f47128be0 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -269,7 +269,7 @@ final class InputMonitor { flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags); inputWindowHandle.layoutParamsFlags = flags; inputWindowHandle.layoutParamsType = type; - inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); + inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis(); inputWindowHandle.visible = isVisible; inputWindowHandle.canReceiveKeys = child.canReceiveKeys(); inputWindowHandle.hasFocus = hasFocus; @@ -385,7 +385,7 @@ final class InputMonitor { } else { final InputApplicationHandle handle = newApp.mInputApplicationHandle; handle.name = newApp.toString(); - handle.dispatchingTimeoutNanos = newApp.mInputDispatchingTimeoutNanos; + handle.dispatchingTimeoutMillis = newApp.mInputDispatchingTimeoutMillis; mService.mInputManager.setFocusedApplication(mDisplayId, handle); } @@ -570,8 +570,7 @@ final class InputMonitor { final String name, final int type, final boolean isVisible) { inputWindowHandle.name = name; inputWindowHandle.layoutParamsType = type; - inputWindowHandle.dispatchingTimeoutNanos = - WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + inputWindowHandle.dispatchingTimeoutMillis = 0; // it should never receive input inputWindowHandle.visible = isVisible; inputWindowHandle.canReceiveKeys = false; inputWindowHandle.hasFocus = false; diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index 28dcbcdf3cc7..dccd3a669827 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.view.SurfaceControl.HIDDEN; import android.graphics.Point; @@ -217,8 +218,7 @@ public class Letterbox { | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_SLIPPERY; mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; - mWindowHandle.dispatchingTimeoutNanos = - WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; mWindowHandle.visible = true; mWindowHandle.ownerPid = Process.myPid(); mWindowHandle.ownerUid = Process.myUid(); diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index c7a438d527ad..8ef57f726658 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -33,11 +33,11 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTAS import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED; import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; import static com.android.server.wm.Task.LOCK_TASK_AUTH_PINNABLE; -import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED; import android.annotation.NonNull; import android.annotation.Nullable; @@ -264,12 +264,12 @@ public class LockTaskController { } /** - * @return whether the requested task is allowed to be locked (either whitelisted, or declares + * @return whether the requested task is allowed to be locked (either allowlisted, or declares * lockTaskMode="always" in the manifest). */ - boolean isTaskWhitelisted(Task task) { + boolean isTaskAllowlisted(Task task) { switch(task.mLockTaskAuth) { - case LOCK_TASK_AUTH_WHITELISTED: + case LOCK_TASK_AUTH_ALLOWLISTED: case LOCK_TASK_AUTH_LAUNCHABLE: case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return true; @@ -311,7 +311,7 @@ public class LockTaskController { private boolean isLockTaskModeViolationInternal(Task task, boolean isNewClearTask) { // TODO: Double check what's going on here. If the task is already in lock task mode, it's - // likely whitelisted, so will return false below. + // likely allowlisted, so will return false below. if (isTaskLocked(task) && !isNewClearTask) { // If the task is already at the top and won't be cleared, then allow the operation return false; @@ -327,7 +327,7 @@ public class LockTaskController { return false; } - return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty()); + return !(isTaskAllowlisted(task) || mLockTaskModeTasks.isEmpty()); } private boolean isRecentsAllowed(int userId) { @@ -356,7 +356,7 @@ public class LockTaskController { return false; default: } - return isPackageWhitelisted(userId, packageName); + return isPackageAllowlisted(userId, packageName); } private boolean isEmergencyCallTask(Task task) { @@ -556,7 +556,7 @@ public class LockTaskController { if (!isSystemCaller) { task.mLockTaskUid = callingUid; if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) { - // startLockTask() called by app, but app is not part of lock task whitelist. Show + // startLockTask() called by app, but app is not part of lock task allowlist. Show // app pinning request. We will come back here with isSystemCaller true. if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user"); StatusBarManagerInternal statusBarManager = LocalServices.getService( @@ -649,8 +649,8 @@ public class LockTaskController { /** * Update packages that are allowed to be launched in lock task mode. - * @param userId Which user this whitelist is associated with - * @param packages The whitelist of packages allowed in lock task mode + * @param userId Which user this allowlist is associated with + * @param packages The allowlist of packages allowed in lock task mode * @see #mLockTaskPackages */ void updateLockTaskPackages(int userId, String[] packages) { @@ -659,19 +659,19 @@ public class LockTaskController { boolean taskChanged = false; for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) { final Task lockedTask = mLockTaskModeTasks.get(taskNdx); - final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE - || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; + final boolean wasAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED; lockedTask.setLockTaskAuth(); - final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE - || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; + final boolean isAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED; if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED || lockedTask.mUserId != userId - || !wasWhitelisted || isWhitelisted) { + || !wasAllowlisted || isAllowlisted) { continue; } - // Terminate locked tasks that have recently lost whitelist authorization. + // Terminate locked tasks that have recently lost allowlist authorization. if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " + lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString()); removeLockedTask(lockedTask); @@ -697,17 +697,17 @@ public class LockTaskController { } } - boolean isPackageWhitelisted(int userId, String pkg) { + boolean isPackageAllowlisted(int userId, String pkg) { if (pkg == null) { return false; } - String[] whitelist; - whitelist = mLockTaskPackages.get(userId); - if (whitelist == null) { + String[] allowlist; + allowlist = mLockTaskPackages.get(userId); + if (allowlist == null) { return false; } - for (String whitelistedPkg : whitelist) { - if (pkg.equals(whitelistedPkg)) { + for (String allowlistedPkg : allowlist) { + if (pkg.equals(allowlistedPkg)) { return true; } } diff --git a/services/core/java/com/android/server/wm/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java index 0f92bc83a666..61b6e0b25961 100644 --- a/services/core/java/com/android/server/wm/PolicyControl.java +++ b/services/core/java/com/android/server/wm/PolicyControl.java @@ -196,40 +196,40 @@ class PolicyControl { private static final String ALL = "*"; private static final String APPS = "apps"; - private final ArraySet<String> mWhitelist; - private final ArraySet<String> mBlacklist; + private final ArraySet<String> mAllowlist; + private final ArraySet<String> mDenylist; - private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) { - mWhitelist = whitelist; - mBlacklist = blacklist; + private Filter(ArraySet<String> allowlist, ArraySet<String> denylist) { + mAllowlist = allowlist; + mDenylist = denylist; } boolean matches(LayoutParams attrs) { if (attrs == null) return false; boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; - if (isApp && mBlacklist.contains(APPS)) return false; - if (onBlacklist(attrs.packageName)) return false; - if (isApp && mWhitelist.contains(APPS)) return true; - return onWhitelist(attrs.packageName); + if (isApp && mDenylist.contains(APPS)) return false; + if (onDenylist(attrs.packageName)) return false; + if (isApp && mAllowlist.contains(APPS)) return true; + return onAllowlist(attrs.packageName); } boolean matches(String packageName) { - return !onBlacklist(packageName) && onWhitelist(packageName); + return !onDenylist(packageName) && onAllowlist(packageName); } - private boolean onBlacklist(String packageName) { - return mBlacklist.contains(packageName) || mBlacklist.contains(ALL); + private boolean onDenylist(String packageName) { + return mDenylist.contains(packageName) || mDenylist.contains(ALL); } - private boolean onWhitelist(String packageName) { - return mWhitelist.contains(ALL) || mWhitelist.contains(packageName); + private boolean onAllowlist(String packageName) { + return mAllowlist.contains(ALL) || mAllowlist.contains(packageName); } void dump(PrintWriter pw) { pw.print("Filter["); - dump("whitelist", mWhitelist, pw); pw.print(','); - dump("blacklist", mBlacklist, pw); pw.print(']'); + dump("allowlist", mAllowlist, pw); pw.print(','); + dump("denylist", mDenylist, pw); pw.print(']'); } private void dump(String name, ArraySet<String> set, PrintWriter pw) { @@ -253,18 +253,18 @@ class PolicyControl { // e.g. "com.package1", or "apps, com.android.keyguard" or "*" static Filter parse(String value) { if (value == null) return null; - ArraySet<String> whitelist = new ArraySet<String>(); - ArraySet<String> blacklist = new ArraySet<String>(); + ArraySet<String> allowlist = new ArraySet<String>(); + ArraySet<String> denylist = new ArraySet<String>(); for (String token : value.split(",")) { token = token.trim(); if (token.startsWith("-") && token.length() > 1) { token = token.substring(1); - blacklist.add(token); + denylist.add(token); } else { - whitelist.add(token); + allowlist.add(token); } } - return new Filter(whitelist, blacklist); + return new Filter(allowlist, denylist); } } } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index ba2c0b6dc0ac..df5356303f8b 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -655,7 +655,8 @@ class RecentTasks { } for (int i = mTasks.size() - 1; i >= 0; --i) { final Task task = mTasks.get(i); - if (task.mUserId == userId && !mService.getLockTaskController().isTaskWhitelisted(task)) { + if (task.mUserId == userId + && !mService.getLockTaskController().isTaskAllowlisted(task)) { remove(task); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ac96d144c4d0..1cb483c1d1a0 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2571,7 +2571,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mDisplayAccessUIDs.clear(); for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { final DisplayContent displayContent = getChildAt(displayNdx); - // Only bother calculating the whitelist for private displays + // Only bother calculating the allowlist for private displays if (displayContent.isPrivate()) { mDisplayAccessUIDs.append( displayContent.mDisplayId, displayContent.getPresentUIDs()); diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java index b71ecbb8a72d..ede6708d5f8f 100644 --- a/services/core/java/com/android/server/wm/SafeActivityOptions.java +++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java @@ -233,10 +233,10 @@ public class SafeActivityOptions { Slog.w(TAG, msg); throw new SecurityException(msg); } - // Check if someone tries to launch an unwhitelisted activity into LockTask mode. + // Check if someone tries to launch an unallowlisted activity into LockTask mode. final boolean lockTaskMode = options.getLockTaskMode(); if (aInfo != null && lockTaskMode - && !supervisor.mService.getLockTaskController().isPackageWhitelisted( + && !supervisor.mService.getLockTaskController().isPackageAllowlisted( UserHandle.getUserId(callingUid), aInfo.packageName)) { final String msg = "Permission Denial: starting " + getIntentString(intent) + " from " + callerApp + " (pid=" + callingPid diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f3620050bed2..be0815b06051 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -45,7 +45,7 @@ import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; @@ -210,7 +210,6 @@ import android.window.ITaskOrganizer; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; -import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledFunction; @@ -411,7 +410,7 @@ class Task extends WindowContainer<WindowContainer> { /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */ final static int LOCK_TASK_AUTH_LAUNCHABLE = 2; /** Can enter lockTask without user approval. Can start over existing lockTask task. */ - final static int LOCK_TASK_AUTH_WHITELISTED = 3; + final static int LOCK_TASK_AUTH_ALLOWLISTED = 3; /** Priv-app that starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing * lockTask task. */ final static int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4; @@ -1686,7 +1685,7 @@ class Task extends WindowContainer<WindowContainer> { getDisplayArea().addStackReferenceIfNeeded((Task) child); } - // Make sure the list of display UID whitelists is updated + // Make sure the list of display UID allowlists is updated // now that this record is in a new task. mRootWindowContainer.updateUIDsPresentOnDisplay(); @@ -1903,7 +1902,7 @@ class Task extends WindowContainer<WindowContainer> { case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK"; case LOCK_TASK_AUTH_PINNABLE: return "LOCK_TASK_AUTH_PINNABLE"; case LOCK_TASK_AUTH_LAUNCHABLE: return "LOCK_TASK_AUTH_LAUNCHABLE"; - case LOCK_TASK_AUTH_WHITELISTED: return "LOCK_TASK_AUTH_WHITELISTED"; + case LOCK_TASK_AUTH_ALLOWLISTED: return "LOCK_TASK_AUTH_ALLOWLISTED"; case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return "LOCK_TASK_AUTH_LAUNCHABLE_PRIV"; default: return "unknown=" + mLockTaskAuth; } @@ -1923,8 +1922,8 @@ class Task extends WindowContainer<WindowContainer> { final LockTaskController lockTaskController = mAtmService.getLockTaskController(); switch (r.lockTaskLaunchMode) { case LOCK_TASK_LAUNCH_MODE_DEFAULT: - mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg) - ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE; + mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg) + ? LOCK_TASK_AUTH_ALLOWLISTED : LOCK_TASK_AUTH_PINNABLE; break; case LOCK_TASK_LAUNCH_MODE_NEVER: @@ -1935,8 +1934,8 @@ class Task extends WindowContainer<WindowContainer> { mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV; break; - case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: - mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg) + case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED: + mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg) ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE; break; } @@ -2366,7 +2365,6 @@ class Task extends WindowContainer<WindowContainer> { private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) { if (mWmService.mDisableTransitionAnimation || !isVisible() - || getDisplayContent().mAppTransition.isTransitionSet() || getSurfaceControl() == null || !isLeafTask()) { return false; @@ -7372,8 +7370,6 @@ class Task extends WindowContainer<WindowContainer> { getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */); mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this); - MetricsLoggerWrapper.logPictureInPictureFullScreen(mAtmService.mContext, - task.effectiveUid, task.realActivity.flattenToString()); }); } diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index c68b660bb76f..3fbc0373e1a9 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.RESIZE_MODE_USER; import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED; +import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM; @@ -230,8 +231,8 @@ class TaskPositioner implements IBinder.DeathRecipient { mDragApplicationHandle = new InputApplicationHandle(new Binder()); mDragApplicationHandle.name = TAG; - mDragApplicationHandle.dispatchingTimeoutNanos = - WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + mDragApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; + mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, displayContent.getDisplayId()); @@ -239,8 +240,7 @@ class TaskPositioner implements IBinder.DeathRecipient { mDragWindowHandle.token = mServerChannel.getToken(); mDragWindowHandle.layoutParamsFlags = 0; mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; - mDragWindowHandle.dispatchingTimeoutNanos = - WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; mDragWindowHandle.visible = true; mDragWindowHandle.canReceiveKeys = false; mDragWindowHandle.hasFocus = true; diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index f3c7a5dcb6d5..9b18ac8f7702 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.graphics.Color.WHITE; import static android.graphics.Color.alpha; import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; @@ -142,6 +143,7 @@ class TaskSnapshotSurface implements StartingSurface { private final Handler mHandler; private boolean mSizeMismatch; private final Paint mBackgroundPaint = new Paint(); + private final int mActivityType; private final int mStatusBarColor; @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter; private final int mOrientationOnCreation; @@ -173,6 +175,7 @@ class TaskSnapshotSurface implements StartingSurface { final int windowFlags; final int windowPrivateFlags; final int currentOrientation; + final int activityType; final InsetsState insetsState; synchronized (service.mGlobalLock) { final WindowState mainWindow = activity.findMainWindow(); @@ -241,6 +244,7 @@ class TaskSnapshotSurface implements StartingSurface { taskBounds = new Rect(); task.getBounds(taskBounds); currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation; + activityType = activity.getActivityType(); final InsetsPolicy insetsPolicy = topFullscreenOpaqueWindow.getDisplayContent() .getInsetsPolicy(); @@ -261,7 +265,8 @@ class TaskSnapshotSurface implements StartingSurface { } final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window, surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis, - windowFlags, windowPrivateFlags, taskBounds, currentOrientation, insetsState); + windowFlags, windowPrivateFlags, taskBounds, currentOrientation, activityType, + insetsState); window.setOuter(snapshotSurface); try { session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1, @@ -282,7 +287,7 @@ class TaskSnapshotSurface implements StartingSurface { TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl, TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds, - int currentOrientation, InsetsState insetsState) { + int currentOrientation, int activityType, InsetsState insetsState) { mService = service; mSurface = service.mSurfaceFactory.get(); mHandler = new Handler(mService.mH.getLooper()); @@ -298,6 +303,7 @@ class TaskSnapshotSurface implements StartingSurface { windowPrivateFlags, sysUiVis, taskDescription, 1f, insetsState); mStatusBarColor = taskDescription.getStatusBarColor(); mOrientationOnCreation = currentOrientation; + mActivityType = activityType; mTransaction = mService.mTransactionFactory.get(); } @@ -305,7 +311,9 @@ class TaskSnapshotSurface implements StartingSurface { public void remove() { synchronized (mService.mGlobalLock) { final long now = SystemClock.uptimeMillis(); - if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) { + if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS + // Show the latest content as soon as possible for unlocking to home. + && mActivityType != ACTIVITY_TYPE_HOME) { mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS); ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Defer removing snapshot surface in %dms", (now - mShownTime)); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ede92f0cad41..0b1d6bc0adfd 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -35,6 +35,7 @@ import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; import static android.content.pm.PackageManager.FEATURE_PC; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; import static android.os.Process.myPid; @@ -366,9 +367,6 @@ public class WindowManagerService extends IWindowManager.Stub // proceding with safe mode detection. private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000; - // Default input dispatching timeout in nanoseconds. - static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L; - // Poll interval in milliseconds for watching boot animation finished. // TODO(b/159045990) Migrate to SystemService.waitForState with dedicated thread. private static final int BOOT_ANIMATION_POLL_INTERVAL = 50; @@ -8090,7 +8088,7 @@ public class WindowManagerService extends IWindowManager.Stub | LayoutParams.FLAG_SLIPPERY); h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags; h.layoutParamsType = type; - h.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; h.canReceiveKeys = false; h.hasFocus = false; h.hasWallpaper = false; diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index df59c56f5aff..a58c5646858b 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.os.Build.VERSION_CODES.Q; +import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.view.Display.INVALID_DISPLAY; import static com.android.server.am.ActivityManagerService.MY_PID; @@ -30,8 +31,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEA import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS; -import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS; -import static com.android.server.wm.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS; +import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.Task.ActivityState.DESTROYED; import static com.android.server.wm.Task.ActivityState.DESTROYING; @@ -41,6 +41,7 @@ import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.Task.ActivityState.STARTED; import static com.android.server.wm.Task.ActivityState.STOPPING; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1064,10 +1065,16 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return RELAUNCH_REASON_NONE; } - public long getInputDispatchingTimeout() { + /** + * Get the current dispatching timeout. If instrumentation is currently taking place, return + * a longer value. Shorter timeout is returned otherwise. + * @return The timeout in milliseconds + */ + public long getInputDispatchingTimeoutMillis() { synchronized (mAtm.mGlobalLock) { return isInstrumenting() || isUsingWrapper() - ? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS : KEY_DISPATCHING_TIMEOUT_MS; + ? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS : + DEFAULT_DISPATCHING_TIMEOUT_MILLIS; } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ef78420a3646..ab78e74b9e37 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.isSplitScreenWindowingMode; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.graphics.GraphicsProtos.dumpPointProto; +import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.PowerManager.DRAW_WAKE_LOCK; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.InsetsState.ITYPE_IME; @@ -1635,10 +1636,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - public long getInputDispatchingTimeoutNanos() { + public long getInputDispatchingTimeoutMillis() { return mActivityRecord != null - ? mActivityRecord.mInputDispatchingTimeoutNanos - : WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + ? mActivityRecord.mInputDispatchingTimeoutMillis + : DEFAULT_DISPATCHING_TIMEOUT_MILLIS; } @Override diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 0e1b2f25c7af..4b5f38c40e4f 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -99,6 +99,7 @@ cc_defaults { "libpowermanager", "libutils", "libui", + "libvibratorservice", "libinput", "libinputflinger", "libinputflinger_base", diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index 05aa3594eb68..74e2328105dc 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -33,6 +33,8 @@ #include <inttypes.h> #include <stdio.h> +#include <vibratorservice/VibratorHalController.h> + using android::hardware::Return; using android::hardware::Void; using android::hardware::vibrator::V1_0::EffectStrength; @@ -226,8 +228,24 @@ bool isValidEffect(jlong effect) { return val >= *iter.begin() && val <= *std::prev(iter.end()); } -static void vibratorInit(JNIEnv *env, jclass clazz) -{ +static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) { + aidl::CompositeEffect effect; + effect.primitive = static_cast<aidl::CompositePrimitive>( + env->GetIntField(primitive, gPrimitiveClassInfo.id)); + effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale)); + effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, gPrimitiveClassInfo.delay)); + return effect; +} + +static void destroyVibratorController(void* rawVibratorController) { + vibrator::HalController* vibratorController = + reinterpret_cast<vibrator::HalController*>(rawVibratorController); + if (vibratorController) { + delete vibratorController; + } +} + +static jlong vibratorInit(JNIEnv* /* env */, jclass /* clazz */) { if (auto hal = getHal<aidl::IVibrator>()) { // IBinder::pingBinder isn't accessible as a pointer function // but getCapabilities can serve the same purpose @@ -236,25 +254,26 @@ static void vibratorInit(JNIEnv *env, jclass clazz) } else { halCall(&V1_0::IVibrator::ping).isOk(); } + std::unique_ptr<vibrator::HalController> controller = + std::make_unique<vibrator::HalController>(); + controller->init(); + return reinterpret_cast<jlong>(controller.release()); } -static jboolean vibratorExists(JNIEnv* /* env */, jclass /* clazz */) -{ - bool ok; +static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyVibratorController)); +} - if (auto hal = getHal<aidl::IVibrator>()) { - // IBinder::pingBinder isn't accessible as a pointer function - // but getCapabilities can serve the same purpose - int32_t cap; - ok = hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk(); - } else { - ok = halCall(&V1_0::IVibrator::ping).isOk(); +static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorExists failed because controller was not initialized"); + return JNI_FALSE; } - return ok ? JNI_TRUE : JNI_FALSE; + return controller->ping().isOk() ? JNI_TRUE : JNI_FALSE; } -static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms) -{ +static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms) { if (auto hal = getHal<aidl::IVibrator>()) { auto status = hal->call(&aidl::IVibrator::on, timeout_ms, nullptr); if (!status.isOk()) { @@ -268,93 +287,53 @@ static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms) } } -static void vibratorOff(JNIEnv* /* env */, jclass /* clazz */) -{ - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::off); - if (!status.isOk()) { - ALOGE("vibratorOff command failed: %s", status.toString8().string()); - } - } else { - Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR); - if (retStatus != Status::OK) { - ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus)); - } - } -} - -static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jclass) { - if (auto hal = getHal<aidl::IVibrator>()) { - int32_t cap = 0; - if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) { - return false; - } - return (cap & aidl::IVibrator::CAP_AMPLITUDE_CONTROL) > 0; - } else { - return halCall(&V1_0::IVibrator::supportsAmplitudeControl).withDefault(false); +static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorOff failed because controller was not initialized"); + return; } + controller->off(); } -static void vibratorSetAmplitude(JNIEnv*, jclass, jint amplitude) { - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::IVibrator::setAmplitude, static_cast<float>(amplitude) / UINT8_MAX); - if (!status.isOk()) { - ALOGE("Failed to set vibrator amplitude: %s", status.toString8().string()); - } - } else { - Status status = halCall(&V1_0::IVibrator::setAmplitude, static_cast<uint32_t>(amplitude)) - .withDefault(Status::UNKNOWN_ERROR); - if (status != Status::OK) { - ALOGE("Failed to set vibrator amplitude (%" PRIu32 ").", - static_cast<uint32_t>(status)); - } +static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, + jint amplitude) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorSetAmplitude failed because controller was not initialized"); + return; } + controller->setAmplitude(static_cast<int32_t>(amplitude)); } -static jboolean vibratorSupportsExternalControl(JNIEnv*, jclass) { - if (auto hal = getHal<aidl::IVibrator>()) { - int32_t cap = 0; - if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) { - return false; - } - return (cap & aidl::IVibrator::CAP_EXTERNAL_CONTROL) > 0; - } else { - return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false); +static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, + jboolean enabled) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorSetExternalControl failed because controller was not initialized"); + return; } + controller->setExternalControl(enabled); } -static void vibratorSetExternalControl(JNIEnv*, jclass, jboolean enabled) { - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::IVibrator::setExternalControl, enabled); - if (!status.isOk()) { - ALOGE("Failed to set vibrator external control: %s", status.toString8().string()); - } - } else { - Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast<uint32_t>(enabled)) - .withDefault(Status::UNKNOWN_ERROR); - if (status != Status::OK) { - ALOGE("Failed to set vibrator external control (%" PRIu32 ").", - static_cast<uint32_t>(status)); - } +static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorGetSupportedEffects failed because controller was not initialized"); + return nullptr; } -} - -static jintArray vibratorGetSupportedEffects(JNIEnv *env, jclass) { - if (auto hal = getHal<aidl::IVibrator>()) { - std::vector<aidl::Effect> supportedEffects; - if (!hal->call(&aidl::IVibrator::getSupportedEffects, &supportedEffects).isOk()) { - return nullptr; - } - jintArray arr = env->NewIntArray(supportedEffects.size()); - env->SetIntArrayRegion(arr, 0, supportedEffects.size(), - reinterpret_cast<jint*>(supportedEffects.data())); - return arr; - } else { + auto result = controller->getSupportedEffects(); + if (!result.isOk()) { return nullptr; } + std::vector<aidl::Effect> supportedEffects = result.value(); + jintArray effects = env->NewIntArray(supportedEffects.size()); + env->SetIntArrayRegion(effects, 0, supportedEffects.size(), + reinterpret_cast<jint*>(supportedEffects.data())); + return effects; } -static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength, +static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong effect, jlong strength, jobject vibration, jboolean withCallback) { if (auto hal = getHal<aidl::IVibrator>()) { int32_t lengthMs; @@ -420,17 +399,8 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong stre return -1; } -static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) { - aidl::CompositeEffect effect; - effect.primitive = static_cast<aidl::CompositePrimitive>( - env->GetIntField(primitive, gPrimitiveClassInfo.id)); - effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale)); - effect.delayMs = static_cast<int>(env->GetIntField(primitive, gPrimitiveClassInfo.delay)); - return effect; -} - -static void vibratorPerformComposedEffect(JNIEnv* env, jclass, jobjectArray composition, - jobject vibration) { +static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jobjectArray composition, + jobject vibration) { auto hal = getHal<aidl::IVibrator>(); if (!hal) { return; @@ -451,65 +421,71 @@ static void vibratorPerformComposedEffect(JNIEnv* env, jclass, jobjectArray comp } } -static jlong vibratorGetCapabilities(JNIEnv*, jclass) { - if (auto hal = getHal<aidl::IVibrator>()) { - int32_t cap = 0; - if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) { - return 0; - } - return cap; +static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorGetCapabilities failed because controller was not initialized"); + return 0; } - - return 0; + auto result = controller->getCapabilities(); + return result.isOk() ? static_cast<jlong>(result.value()) : 0; } -static void vibratorAlwaysOnEnable(JNIEnv* env, jclass, jlong id, jlong effect, jlong strength) { - auto status = halCall(&aidl::IVibrator::alwaysOnEnable, id, - static_cast<aidl::Effect>(effect), static_cast<aidl::EffectStrength>(strength)); - if (!status.isOk()) { - ALOGE("vibratortAlwaysOnEnable command failed (%s).", status.toString8().string()); +static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong id, + jlong effect, jlong strength) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorAlwaysOnEnable failed because controller was not initialized"); + return; } + controller->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect), + static_cast<aidl::EffectStrength>(strength)); } -static void vibratorAlwaysOnDisable(JNIEnv* env, jclass, jlong id) { - auto status = halCall(&aidl::IVibrator::alwaysOnDisable, id); - if (!status.isOk()) { - ALOGE("vibratorAlwaysOnDisable command failed (%s).", status.toString8().string()); +static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, + jlong id) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorAlwaysOnDisable failed because controller was not initialized"); + return; } + controller->alwaysOnDisable(static_cast<int32_t>(id)); } static const JNINativeMethod method_table[] = { - {"vibratorExists", "()Z", (void*)vibratorExists}, - {"vibratorInit", "()V", (void*)vibratorInit}, + {"vibratorInit", "()J", (void*)vibratorInit}, + {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer}, + {"vibratorExists", "(J)Z", (void*)vibratorExists}, {"vibratorOn", "(J)V", (void*)vibratorOn}, - {"vibratorOff", "()V", (void*)vibratorOff}, - {"vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl}, - {"vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude}, + {"vibratorOff", "(J)V", (void*)vibratorOff}, + {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude}, {"vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;Z)J", (void*)vibratorPerformEffect}, {"vibratorPerformComposedEffect", "([Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/" "VibratorService$Vibration;)V", (void*)vibratorPerformComposedEffect}, - {"vibratorGetSupportedEffects", "()[I", (void*)vibratorGetSupportedEffects}, - {"vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl}, - {"vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl}, - {"vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities}, - {"vibratorAlwaysOnEnable", "(JJJ)V", (void*)vibratorAlwaysOnEnable}, - {"vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable}, + {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects}, + {"vibratorSetExternalControl", "(JZ)V", (void*)vibratorSetExternalControl}, + {"vibratorGetCapabilities", "(J)J", (void*)vibratorGetCapabilities}, + {"vibratorAlwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable}, + {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable}, }; int register_android_server_VibratorService(JNIEnv *env) { - sMethodIdOnComplete = GetMethodIDOrDie(env, - FindClassOrDie(env, "com/android/server/VibratorService$Vibration"), - "onComplete", "()V"); - jclass primitiveClass = FindClassOrDie(env, - "android/os/VibrationEffect$Composition$PrimitiveEffect"); + sMethodIdOnComplete = + GetMethodIDOrDie(env, + FindClassOrDie(env, "com/android/server/VibratorService$Vibration"), + "onComplete", "()V"); + + jclass primitiveClass = + FindClassOrDie(env, "android/os/VibrationEffect$Composition$PrimitiveEffect"); gPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I"); gPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F"); gPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I"); - return jniRegisterNativeMethods(env, "com/android/server/VibratorService", - method_table, NELEM(method_table)); + + return jniRegisterNativeMethods(env, "com/android/server/VibratorService", method_table, + NELEM(method_table)); } -}; +}; // namespace android diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 784366318319..0202c88009fd 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -710,9 +710,8 @@ std::chrono::nanoseconds NativeInputManager::notifyAnr( jobject tokenObj = javaObjectForIBinder(env, token); jstring reasonObj = env->NewStringUTF(reason.c_str()); - jlong newTimeout = env->CallLongMethod(mServiceObj, - gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj, - reasonObj); + jlong newTimeout = env->CallLongMethod(mServiceObj, gServiceClassInfo.notifyANR, + inputApplicationHandleObj, tokenObj, reasonObj); if (checkAndClearExceptionFromCallback(env, "notifyANR")) { newTimeout = 0; // abort dispatch } else { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index c11c4b2e25c1..97ae505b4fcf 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -121,9 +121,7 @@ import com.android.server.inputmethod.MultiClientInputMethodManagerService; import com.android.server.integrity.AppIntegrityManagerService; import com.android.server.lights.LightsService; import com.android.server.location.LocationManagerService; -import com.android.server.media.MediaResourceMonitorService; import com.android.server.media.MediaRouterService; -import com.android.server.media.MediaSessionService; import com.android.server.media.projection.MediaProjectionManagerService; import com.android.server.net.NetworkPolicyManagerService; import com.android.server.net.NetworkStatsService; @@ -312,6 +310,10 @@ public final class SystemServer { "com.android.server.rollback.RollbackManagerService"; private static final String ALARM_MANAGER_SERVICE_CLASS = "com.android.server.alarm.AlarmManagerService"; + private static final String MEDIA_SESSION_SERVICE_CLASS = + "com.android.server.media.MediaSessionService"; + private static final String MEDIA_RESOURCE_MONITOR_SERVICE_CLASS = + "com.android.server.media.MediaResourceMonitorService"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; @@ -1893,7 +1895,7 @@ public final class SystemServer { t.traceEnd(); t.traceBegin("StartMediaSessionService"); - mSystemServiceManager.startService(MediaSessionService.class); + mSystemServiceManager.startService(MEDIA_SESSION_SERVICE_CLASS); t.traceEnd(); if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) { @@ -1917,7 +1919,7 @@ public final class SystemServer { if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) { t.traceBegin("StartMediaResourceMonitor"); - mSystemServiceManager.startService(MediaResourceMonitorService.class); + mSystemServiceManager.startService(MEDIA_RESOURCE_MONITOR_SERVICE_CLASS); t.traceEnd(); } diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 37bf66491882..33317a38853e 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -19,6 +19,8 @@ package com.android.server.people; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.people.ConversationChannel; +import android.app.people.IPeopleManager; import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionSessionId; import android.app.prediction.AppTarget; @@ -26,8 +28,11 @@ import android.app.prediction.AppTargetEvent; import android.app.prediction.IPredictionCallback; import android.content.Context; import android.content.pm.ParceledListSlice; +import android.os.Binder; import android.os.CancellationSignal; +import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.Slog; @@ -35,6 +40,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemService; import com.android.server.people.data.DataManager; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -68,6 +74,7 @@ public class PeopleService extends SystemService { @Override public void onStart() { + publishBinderService(Context.PEOPLE_SERVICE, new BinderService()); publishLocalService(PeopleServiceInternal.class, new LocalService()); } @@ -81,6 +88,38 @@ public class PeopleService extends SystemService { mDataManager.onUserStopping(user.getUserIdentifier()); } + /** + * Enforces that only the system or root UID can make certain calls. + * + * @param message used as message if SecurityException is thrown + * @throws SecurityException if the caller is not system or root + */ + private static void enforceSystemOrRoot(String message) { + int uid = Binder.getCallingUid(); + if (!UserHandle.isSameApp(uid, Process.SYSTEM_UID) && uid != Process.ROOT_UID) { + throw new SecurityException("Only system may " + message); + } + } + + private final class BinderService extends IPeopleManager.Stub { + + @Override + public ParceledListSlice<ConversationChannel> getRecentConversations() { + enforceSystemOrRoot("get recent conversations"); + return new ParceledListSlice<>(new ArrayList<>()); + } + + @Override + public void removeRecentConversation(String packageName, int userId, String shortcutId) { + enforceSystemOrRoot("remove a recent conversation"); + } + + @Override + public void removeAllRecentConversations() { + enforceSystemOrRoot("remove all recent conversations"); + } + } + @VisibleForTesting final class LocalService extends PeopleServiceInternal { diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt index ecdb30f5e84b..09552082e4af 100644 --- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt +++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt @@ -33,7 +33,6 @@ import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.parsing.pkg.ParsedPackage import com.android.server.pm.permission.PermissionManagerServiceInternal import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType -import com.android.server.pm.test.override.R import com.android.server.testutils.TestHandler import com.android.server.testutils.mock import com.android.server.testutils.mockThrowOnUnmocked @@ -266,7 +265,7 @@ class PackageManagerComponentLabelIconOverrideTest { .hideAsFinal() private fun makePkgSetting(pkgName: String) = spy(PackageSetting(pkgName, null, File("/test"), - File("/test"), null, null, null, null, 0, 0, 0, 0, null, null, null)) { + null, null, null, null, 0, 0, 0, 0, null, null, null)) { this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp } diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp index 41dfade1a09a..cffcdd8f94bd 100644 --- a/services/tests/PackageManagerServiceTests/host/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/Android.bp @@ -25,17 +25,28 @@ java_test_host { ], test_suites: ["general-tests"], java_resources: [ - ":PackageManagerDummyAppVersion1", - ":PackageManagerDummyAppVersion2", - ":PackageManagerDummyAppVersion3", - ":PackageManagerDummyAppVersion4", - ":PackageManagerDummyAppOriginalOverride", - ":PackageManagerServiceHostTestsResources", - ] + ":PackageManagerTestAppStub", + ":PackageManagerTestAppVersion1", + ":PackageManagerTestAppVersion2", + ":PackageManagerTestAppVersion3", + ":PackageManagerTestAppVersion3Invalid", + ":PackageManagerTestAppVersion4", + ":PackageManagerTestAppOriginalOverride", + ], } -filegroup { - name: "PackageManagerServiceHostTestsResources", - srcs: [ "resources/*" ], - path: "resources/" +genrule { + name: "PackageManagerTestAppVersion3Invalid", + tools: [ + "soong_zip", + "zipalign", + ], + srcs: [ + ":PackageManagerTestAppVersion3", + ], + out: ["PackageManagerTestAppVersion3Invalid.apk"], + cmd: "mkdir -p $(genDir)/apk && unzip $(in) -d $(genDir)/apk" + + " && truncate -s 800 $(genDir)/apk/META-INF/CERT.RSA" + + " && $(location soong_zip) -o $(genDir)/temp.apk -L 0 -C $(genDir)/apk -D $(genDir)/apk" + + " && $(location zipalign) -f 4 $(genDir)/temp.apk $(out)", } diff --git a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk b/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk Binary files differdeleted file mode 100644 index 127886cf8e9e..000000000000 --- a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk +++ /dev/null diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt index 234fcf19062d..8dfefaf9750f 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt @@ -21,8 +21,9 @@ import com.android.tradefed.device.ITestDevice import java.io.File import java.io.FileOutputStream -internal fun SystemPreparer.pushApk(file: String, partition: Partition) = - pushResourceFile(file, HostUtils.makePathForApk(file, partition).toString()) +internal fun SystemPreparer.pushApk(javaResourceName: String, partition: Partition) = + pushResourceFile(javaResourceName, HostUtils.makePathForApk(javaResourceName, partition) + .toString()) internal fun SystemPreparer.deleteApkFolders( partition: Partition, @@ -58,4 +59,55 @@ internal object HostUtils { } return file } + + /** + * dumpsys package and therefore device.getAppPackageInfo doesn't work immediately after reboot, + * so the following methods parse the package dump directly to see if the path matches. + */ + fun getCodePaths(device: ITestDevice, pkgName: String) = + device.executeShellCommand("pm dump $pkgName") + .lineSequence() + .map(String::trim) + .filter { it.startsWith("codePath=") } + .map { it.removePrefix("codePath=") } + .toList() + + private fun userIdLineSequence(device: ITestDevice, pkgName: String) = + device.executeShellCommand("pm dump $pkgName") + .lineSequence() + .dropWhile { !it.startsWith("Packages:") } + .takeWhile { + !it.startsWith("Hidden system packages:") && + !it.startsWith("Queries:") + } + .map(String::trim) + .filter { it.startsWith("User ") } + + fun getUserIdToPkgEnabledState(device: ITestDevice, pkgName: String) = + userIdLineSequence(device, pkgName).associate { + val userId = it.removePrefix("User ") + .takeWhile(Char::isDigit) + .toInt() + val enabled = it.substringAfter("enabled=") + .takeWhile(Char::isDigit) + .toInt() + .let { + when (it) { + 0, 1 -> true + else -> false + } + } + userId to enabled + } + + fun getUserIdToPkgInstalledState(device: ITestDevice, pkgName: String) = + userIdLineSequence(device, pkgName).associate { + val userId = it.removePrefix("User ") + .takeWhile(Char::isDigit) + .toInt() + val installed = it.substringAfter("installed=") + .takeWhile { !it.isWhitespace() } + .toBoolean() + userId to installed + } } diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt index 39b40d8d3195..b7d135991ccd 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt @@ -33,11 +33,11 @@ import org.junit.runner.RunWith class InvalidNewSystemAppTest : BaseHostJUnit4Test() { companion object { - private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app" - private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk" - private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk" - private const val VERSION_THREE_INVALID = "PackageManagerDummyAppVersion3Invalid.apk" - private const val VERSION_FOUR = "PackageManagerDummyAppVersion4.apk" + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk" + private const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk" + private const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk" @get:ClassRule val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) @@ -49,14 +49,14 @@ class InvalidNewSystemAppTest : BaseHostJUnit4Test() { @get:Rule val rules = RuleChain.outerRule(tempFolder).around(preparer)!! - private val filePath = HostUtils.makePathForApk("PackageManagerDummyApp.apk", Partition.PRODUCT) + private val filePath = HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.PRODUCT) @Before @After fun removeApk() { device.uninstallPackage(TEST_PKG_NAME) - device.deleteFile(filePath.parent.toString()) - device.reboot() + preparer.deleteFile(filePath.parent.toString()) + .reboot() } @Test diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt index fb0348c3146b..4ae3ca5f7263 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt @@ -33,11 +33,11 @@ import org.junit.runner.RunWith class OriginalPackageMigrationTest : BaseHostJUnit4Test() { companion object { - private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app" - private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk" - private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk" - private const val VERSION_THREE = "PackageManagerDummyAppVersion3.apk" - private const val NEW_PKG = "PackageManagerDummyAppOriginalOverride.apk" + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk" + private const val VERSION_THREE = "PackageManagerTestAppVersion3.apk" + private const val NEW_PKG = "PackageManagerTestAppOriginalOverride.apk" @get:ClassRule val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) @@ -55,6 +55,7 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() { fun deleteApkFolders() { preparer.deleteApkFolders(Partition.SYSTEM, VERSION_ONE, VERSION_TWO, VERSION_THREE, NEW_PKG) + .reboot() } @Test @@ -99,9 +100,7 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() { } private fun assertCodePath(apk: String) { - // dumpsys package and therefore device.getAppPackageInfo doesn't work here for some reason, - // so parse the package dump directly to see if the path matches. - assertThat(device.executeShellCommand("pm dump $TEST_PKG_NAME")) - .contains(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString()) + assertThat(HostUtils.getCodePaths(device, TEST_PKG_NAME)) + .containsExactly(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString()) } } diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt new file mode 100644 index 000000000000..bc478b0e2c90 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt @@ -0,0 +1,650 @@ +/* + * Copyright (C) 2020 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.test + +import com.android.internal.util.test.SystemPreparer +import com.android.tradefed.device.ITestDevice +import com.android.tradefed.device.UserInfo +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test +import com.google.common.truth.Truth.assertThat +import org.junit.AfterClass +import org.junit.Before +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import java.io.File +import java.util.zip.GZIPOutputStream + +@RunWith(DeviceJUnit4ClassRunner::class) +class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() { + + companion object { + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_STUB = "PackageManagerTestAppStub.apk" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + + /** + * How many total users on device to test, including primary. This will clean up any + * users created specifically for this test. + */ + private const val USER_COUNT = 3 + + /** + * Whether to manually reset state at each test method without rebooting + * for faster iterative development. + */ + private const val DEBUG_NO_REBOOT = false + + @get:ClassRule + val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) + + private val parentClassName = SystemStubMultiUserDisableUninstallTest::class.java.simpleName + + private val deviceCompressedFile = + HostUtils.makePathForApk("$parentClassName.apk", Partition.PRODUCT).parent + .resolve("$parentClassName.apk.gz") + + private val stubFile = + HostUtils.makePathForApk("$parentClassName-Stub.apk", Partition.PRODUCT) + + private val secondaryUsers = mutableListOf<Int>() + private val usersToRemove = mutableListOf<Int>() + private var savedDevice: ITestDevice? = null + private var savedPreparer: SystemPreparer? = null + + private fun setUpUsers(device: ITestDevice) { + if (this.savedDevice != null) return + this.savedDevice = device + secondaryUsers.clear() + secondaryUsers += device.userInfos.values.map(UserInfo::userId).filterNot { it == 0 } + while (secondaryUsers.size < USER_COUNT) { + secondaryUsers += device.createUser(parentClassName + secondaryUsers.size) + .also { usersToRemove += it } + } + } + + @JvmStatic + @AfterClass + fun cleanUp() { + savedDevice ?: return + + usersToRemove.forEach { + savedDevice?.removeUser(it) + } + + savedDevice?.uninstallPackage(TEST_PKG_NAME) + savedDevice?.deleteFile(stubFile.parent.toString()) + savedDevice?.deleteFile(deviceCompressedFile.parent.toString()) + savedDevice?.reboot() + savedDevice = null + + if (DEBUG_NO_REBOOT) { + savedPreparer?.after() + savedPreparer = null + } + } + } + + private val tempFolder = TemporaryFolder() + private val preparer: SystemPreparer = SystemPreparer(tempFolder, + SystemPreparer.RebootStrategy.START_STOP, deviceRebootRule) { this.device } + + @get:Rule + val rules = RuleChain.outerRule(tempFolder).let { + if (DEBUG_NO_REBOOT) { + it!! + } else { + it.around(preparer)!! + } + } + + private var hostCompressedFile: File? = null + + private val previousCodePaths = mutableListOf<String>() + + @Before + fun ensureUserAndCompressStubAndInstall() { + setUpUsers(device) + + val initialized = hostCompressedFile != null + if (!initialized) { + hostCompressedFile = tempFolder.newFile() + hostCompressedFile!!.outputStream().use { + javaClass.classLoader + .getResource(VERSION_ONE)!! + .openStream() + .use { input -> + GZIPOutputStream(it).use { output -> + input.copyTo(output) + } + } + } + } + + device.uninstallPackage(TEST_PKG_NAME) + + if (!initialized || !DEBUG_NO_REBOOT) { + savedPreparer = preparer + preparer.pushResourceFile(VERSION_STUB, stubFile.toString()) + .pushFile(hostCompressedFile, deviceCompressedFile.toString()) + .reboot() + } + + // This test forces the state to installed/enabled for all users, + // since it only tests the uninstall/disable side. + installExisting(User.PRIMARY) + installExisting(User.SECONDARY) + + ensureEnabled() + + // Ensure data app isn't re-installed multiple times by comparing against the original path + val codePath = HostUtils.getCodePaths(device, TEST_PKG_NAME).first() + assertThat(codePath).contains("/data/app") + assertThat(codePath).contains(TEST_PKG_NAME) + + previousCodePaths.clear() + previousCodePaths += codePath + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disablePrimaryFirstAndUninstall() { + toggleEnabled(false, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + toggleEnabled(false, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + device.uninstallPackage(TEST_PKG_NAME) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + } + + @Test + fun disableSecondaryFirstAndUninstall() { + toggleEnabled(false, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + toggleEnabled(false, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + device.uninstallPackage(TEST_PKG_NAME) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstalledEnablePrimaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + device.uninstallPackage(TEST_PKG_NAME) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstalledEnableSecondaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + device.uninstallPackage(TEST_PKG_NAME) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallPrimaryFirstByUserAndInstallExistingPrimaryFirst() { + uninstall(User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + uninstall(User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + installExisting(User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + installExisting(User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallSecondaryFirstByUserAndInstallExistingSecondaryFirst() { + uninstall(User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + uninstall(User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + installExisting(User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + installExisting(User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallUpdatesAndEnablePrimaryFirst() { + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + // TODO: Is this intentional? There is no user argument for this command. + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + // If any user is enabled when uninstalling updates, /data is re-uncompressed + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + // Test enabling secondary to ensure path does not change, even though it's already enabled + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallUpdatesAndEnableSecondaryFirst() { + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + // If any user is enabled when uninstalling updates, /data is re-uncompressed + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstallUpdatesAndEnablePrimaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstallUpdatesAndEnableSecondaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstalledUninstallUpdatesAndEnablePrimaryFirst() { + uninstall(User.PRIMARY) + uninstall(User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + assertState( + primaryInstalled = false, primaryEnabled = false, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstalledUninstallUpdatesAndEnableSecondaryFirst() { + uninstall(User.PRIMARY) + uninstall(User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + assertState( + primaryInstalled = false, primaryEnabled = false, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = false, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + private fun ensureEnabled() { + toggleEnabled(true, User.PRIMARY) + toggleEnabled(true, User.SECONDARY) + + assertThat(HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME).all { it.value }) + .isTrue() + } + + private fun toggleEnabled(enabled: Boolean, user: User, pkgName: String = TEST_PKG_NAME) { + val command = if (enabled) "enable" else "disable" + @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { + User.PRIMARY -> { + device.executeShellCommand("pm $command --user 0 $pkgName") + } + User.SECONDARY -> { + secondaryUsers.forEach { + device.executeShellCommand("pm $command --user $it $pkgName") + } + } + } + } + + private fun uninstall(user: User, pkgName: String = TEST_PKG_NAME) { + @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { + User.PRIMARY -> { + device.executeShellCommand("pm uninstall --user 0 $pkgName") + } + User.SECONDARY -> { + secondaryUsers.forEach { + device.executeShellCommand("pm uninstall --user $it $pkgName") + } + } + } + } + + private fun installExisting(user: User, pkgName: String = TEST_PKG_NAME) { + @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { + User.PRIMARY -> { + device.executeShellCommand("pm install-existing --user 0 $pkgName") + } + User.SECONDARY -> { + secondaryUsers.forEach { + device.executeShellCommand("pm install-existing --user $it $pkgName") + } + } + } + } + + private fun assertState( + primaryInstalled: Boolean, + primaryEnabled: Boolean, + secondaryInstalled: Boolean, + secondaryEnabled: Boolean, + codePaths: List<CodePath> + ) { + HostUtils.getUserIdToPkgInstalledState(device, TEST_PKG_NAME) + .forEach { (userId, installed) -> + if (userId == 0) { + assertThat(installed).isEqualTo(primaryInstalled) + } else { + assertThat(installed).isEqualTo(secondaryInstalled) + } + } + + HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME) + .forEach { (userId, enabled) -> + if (userId == 0) { + assertThat(enabled).isEqualTo(primaryEnabled) + } else { + assertThat(enabled).isEqualTo(secondaryEnabled) + } + } + + assertCodePaths(codePaths.first(), codePaths.getOrNull(1)) + } + + private fun assertCodePaths(firstCodePath: CodePath, secondCodePath: CodePath? = null) { + val codePaths = HostUtils.getCodePaths(device, TEST_PKG_NAME) + assertThat(codePaths).hasSize(listOfNotNull(firstCodePath, secondCodePath).size) + + when (firstCodePath) { + CodePath.SAME -> { + assertThat(codePaths[0]).contains("/data/app") + assertThat(codePaths[0]).contains(TEST_PKG_NAME) + assertThat(codePaths[0]).isEqualTo(previousCodePaths.last()) + } + CodePath.DIFFERENT -> { + assertThat(codePaths[0]).contains("/data/app") + assertThat(codePaths[0]).contains(TEST_PKG_NAME) + assertThat(previousCodePaths).doesNotContain(codePaths[0]) + previousCodePaths.add(codePaths[0]) + } + CodePath.SYSTEM -> assertThat(codePaths[0]).isEqualTo(stubFile.parent.toString()) + } + + when (secondCodePath) { + CodePath.SAME, CodePath.DIFFERENT -> + throw AssertionError("secondDataPath cannot be a data path") + CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString()) + } + } + + enum class User { + /** The primary system user 0 */ + PRIMARY, + + /** + * All other users on the device that are not 0. This is split into an enum so that all + * methods that handle secondary act on all non-system users. Some behaviors only occur + * if a package state is marked for all non-primary users on the device, which can be + * more than just 1. + */ + SECONDARY + } + + enum class CodePath { + /** The data code path hasn't changed */ + SAME, + + /** New data code path */ + DIFFERENT, + + /** The static system code path */ + SYSTEM + } +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp index c9b29275a731..4a3076e4736a 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp @@ -13,26 +13,32 @@ // limitations under the License. android_test_helper_app { - name: "PackageManagerDummyAppVersion1", + name: "PackageManagerTestAppStub", + manifest: "AndroidManifestVersion1.xml", + srcs: [] +} + +android_test_helper_app { + name: "PackageManagerTestAppVersion1", manifest: "AndroidManifestVersion1.xml" } android_test_helper_app { - name: "PackageManagerDummyAppVersion2", + name: "PackageManagerTestAppVersion2", manifest: "AndroidManifestVersion2.xml" } android_test_helper_app { - name: "PackageManagerDummyAppVersion3", + name: "PackageManagerTestAppVersion3", manifest: "AndroidManifestVersion3.xml" } android_test_helper_app { - name: "PackageManagerDummyAppVersion4", + name: "PackageManagerTestAppVersion4", manifest: "AndroidManifestVersion4.xml" } android_test_helper_app { - name: "PackageManagerDummyAppOriginalOverride", + name: "PackageManagerTestAppOriginalOverride", manifest: "AndroidManifestOriginalOverride.xml" } diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml index f16e1bc8a927..cba580e44065 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml @@ -16,10 +16,10 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app.override" + package="com.android.server.pm.test.test_app.override" android:versionCode="2" > - <original-package android:name="com.android.server.pm.test.dummy_app"/> + <original-package android:name="com.android.server.pm.test.test_app"/> </manifest> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml index b492a31349fc..efc7372a177c 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="1" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml index 25e9f8eb2a67..620054c5dd57 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="2" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml index 935f5e62f508..1997771783dd 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="3" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml index d0643cbb2aeb..d6ade0304189 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="4" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index 44eb8285c7db..a398961db4c3 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -17,6 +17,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.mockingservicestests"> + <uses-sdk android:targetSdkVersion="30" /> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 2a267c413c31..1b2711d3938b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -99,6 +99,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -165,7 +166,7 @@ public class MockingOomAdjusterTests { setFieldValue(ActivityManagerService.class, sService, "mHandler", mock(ActivityManagerService.MainHandler.class)); setFieldValue(ActivityManagerService.class, sService, "mProcessStats", - mock(ProcessStatsService.class)); + new ProcessStatsService(sService, new File(sContext.getFilesDir(), "procstats"))); setFieldValue(ActivityManagerService.class, sService, "mBackupTargets", mock(SparseArray.class)); setFieldValue(ActivityManagerService.class, sService, "mOomAdjProfiler", diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java index 0a61c443e0bd..7a3a9504a0b3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java @@ -130,7 +130,9 @@ public class AppOpsServiceTest { } @After - public void resetStaticMocks() { + public void tearDown() { + mAppOpsService.shutdown(); + mMockingSession.finishMocking(); } @@ -216,9 +218,8 @@ public class AppOpsServiceTest { false); mAppOpsService.writeState(); - // Create a new app ops service, and initialize its state from XML. + // Create a new app ops service which will initialize its state from XML. setupAppOpsService(); - mAppOpsService.readState(); // Query the state of the 2nd service. List<PackageOps> loggedOps = getLoggedOps(); @@ -233,9 +234,8 @@ public class AppOpsServiceTest { mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false); mAppOpsService.shutdown(); - // Create a new app ops service, and initialize its state from XML. + // Create a new app ops service which will initialize its state from XML. setupAppOpsService(); - mAppOpsService.readState(); // Query the state of the 2nd service. List<PackageOps> loggedOps = getLoggedOps(); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java new file mode 100644 index 000000000000..1cb004a6dc1e --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java @@ -0,0 +1,971 @@ +/* + * Copyright (C) 2020 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.location; + +import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; +import static android.app.AlarmManager.WINDOW_EXACT; +import static android.app.AppOpsManager.OP_FINE_LOCATION; +import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; +import static android.app.AppOpsManager.OP_MONITOR_LOCATION; +import static android.location.Criteria.ACCURACY_COARSE; +import static android.location.Criteria.ACCURACY_FINE; +import static android.location.Criteria.POWER_HIGH; +import static android.location.LocationManager.PASSIVE_PROVIDER; + +import static androidx.test.ext.truth.location.LocationSubject.assertThat; + +import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; +import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; +import static com.android.server.location.LocationPermissions.PERMISSION_FINE; +import static com.android.server.location.LocationUtils.createLocation; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.after; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.MockitoAnnotations.initMocks; + +import android.app.AlarmManager; +import android.app.AlarmManager.OnAlarmListener; +import android.content.Context; +import android.location.ILocationCallback; +import android.location.ILocationListener; +import android.location.Location; +import android.location.LocationManagerInternal; +import android.location.LocationManagerInternal.ProviderEnabledListener; +import android.location.LocationRequest; +import android.location.util.identity.CallerIdentity; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.ICancellationSignal; +import android.os.IRemoteCallback; +import android.os.PowerManager; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.WorkSource; +import android.platform.test.annotations.Presubmit; +import android.util.Log; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; +import com.android.server.FgThread; +import com.android.server.LocalServices; +import com.android.server.location.listeners.ListenerRegistration; +import com.android.server.location.util.FakeUserInfoHelper; +import com.android.server.location.util.TestInjector; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class LocationProviderManagerTest { + + private static final String TAG = "LocationProviderManagerTest"; + + private static final long TIMEOUT_MS = 1000; + + private static final int CURRENT_USER = FakeUserInfoHelper.DEFAULT_USERID; + private static final int OTHER_USER = CURRENT_USER + 10; + + private static final String NAME = "test"; + private static final ProviderProperties PROPERTIES = new ProviderProperties(false, false, false, + false, true, true, true, POWER_HIGH, ACCURACY_FINE); + private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1, + "mypackage", + "attribution"); + + private Random mRandom; + + @Mock + private LocationManagerInternal mInternal; + @Mock + private Context mContext; + @Mock + private AlarmManager mAlarmManager; + @Mock + private PowerManager mPowerManager; + @Mock + private PowerManager.WakeLock mWakeLock; + + private TestInjector mInjector; + private PassiveLocationProviderManager mPassive; + private TestProvider mProvider; + + private LocationProviderManager mManager; + + @Before + public void setUp() { + initMocks(this); + + long seed = System.currentTimeMillis(); + Log.i(TAG, "location random seed: " + seed); + + mRandom = new Random(seed); + + LocalServices.addService(LocationManagerInternal.class, mInternal); + + doReturn("android").when(mContext).getPackageName(); + doReturn(mAlarmManager).when(mContext).getSystemService(AlarmManager.class); + doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class); + doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString()); + + mInjector = new TestInjector(); + mInjector.getUserInfoHelper().startUser(OTHER_USER); + + mPassive = new PassiveLocationProviderManager(mContext, mInjector); + mPassive.startManager(); + mPassive.setRealProvider(new PassiveProvider(mContext)); + + mProvider = new TestProvider(PROPERTIES, IDENTITY); + mProvider.setProviderAllowed(true); + + mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive); + mManager.startManager(); + mManager.setRealProvider(mProvider); + } + + @After + public void tearDown() throws Exception { + LocalServices.removeServiceForTest(LocationManagerInternal.class); + + // some test failures may leave the fg thread stuck, interrupt until we get out of it + CountDownLatch latch = new CountDownLatch(1); + FgThread.getExecutor().execute(latch::countDown); + int count = 0; + while (++count < 10 && !latch.await(10, TimeUnit.MILLISECONDS)) { + FgThread.get().getLooper().getThread().interrupt(); + } + } + + @Test + public void testProperties() { + assertThat(mManager.getName()).isEqualTo(NAME); + assertThat(mManager.getProperties()).isEqualTo(PROPERTIES); + assertThat(mManager.getIdentity()).isEqualTo(IDENTITY); + assertThat(mManager.hasProvider()).isTrue(); + + ProviderProperties newProperties = new ProviderProperties(true, true, true, + true, false, false, false, POWER_HIGH, ACCURACY_COARSE); + mProvider.setProperties(newProperties); + assertThat(mManager.getProperties()).isEqualTo(newProperties); + + CallerIdentity newIdentity = CallerIdentity.forTest(OTHER_USER, 1, "otherpackage", + "otherattribution"); + mProvider.setIdentity(newIdentity); + assertThat(mManager.getIdentity()).isEqualTo(newIdentity); + + mManager.setRealProvider(null); + assertThat(mManager.hasProvider()).isFalse(); + } + + @Test + public void testRemoveProvider() { + mManager.setRealProvider(null); + assertThat(mManager.hasProvider()).isFalse(); + } + + @Test + public void testIsEnabled() { + assertThat(mManager.isEnabled(CURRENT_USER)).isTrue(); + + mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER); + assertThat(mManager.isEnabled(CURRENT_USER)).isFalse(); + + mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER); + mProvider.setAllowed(false); + assertThat(mManager.isEnabled(CURRENT_USER)).isFalse(); + + mProvider.setAllowed(true); + mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER); + assertThat(mManager.isEnabled(CURRENT_USER)).isFalse(); + assertThat(mManager.isEnabled(OTHER_USER)).isTrue(); + + mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER); + assertThat(mManager.isEnabled(CURRENT_USER)).isTrue(); + assertThat(mManager.isEnabled(OTHER_USER)).isFalse(); + } + + @Test + public void testIsEnabledListener() { + ProviderEnabledListener listener = mock(ProviderEnabledListener.class); + mManager.addEnabledListener(listener); + verify(listener, never()).onProviderEnabledChanged(anyString(), anyInt(), anyBoolean()); + + mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER); + verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, CURRENT_USER, + false); + + mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER); + verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, CURRENT_USER, + true); + + mProvider.setAllowed(false); + verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER, + false); + + mProvider.setAllowed(true); + verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER, + true); + + mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER); + verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER, + false); + verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER, + true); + + mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER); + verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER, + true); + verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER, + false); + + mManager.removeEnabledListener(listener); + mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER); + verifyNoMoreInteractions(listener); + } + + @Test + public void testGetLastLocation_Fine() { + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + + Location loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); + } + + @Test + public void testGetLastLocation_Coarse() { + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + + Location loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + Location coarse = mManager.getLastLocation(request, IDENTITY, PERMISSION_COARSE); + assertThat(coarse).isNotEqualTo(loc); + assertThat(coarse).isNearby(loc, 5000); + } + + @Test + public void testGetLastLocation_Bypass() { + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); + LocationRequest bypassRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, + false).setLocationSettingsIgnored(true); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isNull(); + + Location loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); + assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + loc); + + mProvider.setProviderAllowed(false); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + loc); + + loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + loc); + + mProvider.setProviderAllowed(true); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + loc); + + loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); + assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + loc); + } + + @Test + public void testGetLastLocation_ClearOnMockRemoval() { + MockProvider mockProvider = new MockProvider(PROPERTIES, IDENTITY); + mockProvider.setAllowed(true); + mManager.setMockProvider(mockProvider); + + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); + Location loc = createLocation(NAME, mRandom); + mockProvider.setProviderLocation(loc); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); + + mManager.setMockProvider(null); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + } + + @Test + public void testInjectLastLocation() { + Location loc1 = createLocation(NAME, mRandom); + mManager.injectLastLocation(loc1, CURRENT_USER); + + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc1); + + Location loc2 = createLocation(NAME, mRandom); + mManager.injectLastLocation(loc2, CURRENT_USER); + + assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc1); + } + + @Test + public void testPassive_Listener() throws Exception { + ILocationListener listener = createMockLocationListener(); + LocationRequest request = LocationRequest.createFromDeprecatedProvider(PASSIVE_PROVIDER, 0, + 0, false); + mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + + Location loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + + ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class); + verify(listener).onLocationChanged(locationCaptor.capture(), + nullable(IRemoteCallback.class)); + assertThat(locationCaptor.getValue()).isEqualTo(loc); + } + + @Test + public void testPassive_LastLocation() { + Location loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + + LocationRequest request = LocationRequest.createFromDeprecatedProvider(PASSIVE_PROVIDER, 0, + 0, false); + assertThat(mPassive.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); + } + + @Test + public void testRegisterListener() throws Exception { + ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class); + + ILocationListener listener = createMockLocationListener(); + mManager.registerLocationRequest( + LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), IDENTITY, + PERMISSION_FINE, listener); + + Location loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + verify(listener, times(1)).onLocationChanged(locationCaptor.capture(), + nullable(IRemoteCallback.class)); + assertThat(locationCaptor.getValue()).isEqualTo(loc); + + mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER); + verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, false); + loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + verify(listener, times(1)).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + + mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER); + verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, true); + + mProvider.setAllowed(false); + verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, false); + loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + verify(listener, times(1)).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + + mProvider.setAllowed(true); + verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, true); + + mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER); + verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, false); + loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + verify(listener, times(1)).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + + mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER); + verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, true); + + loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + verify(listener, times(2)).onLocationChanged(locationCaptor.capture(), + nullable(IRemoteCallback.class)); + assertThat(locationCaptor.getValue()).isEqualTo(loc); + } + + @Test + public void testRegisterListener_SameProcess() throws Exception { + ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class); + + CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage", + "attribution"); + + ILocationListener listener = createMockLocationListener(); + mManager.registerLocationRequest( + LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity, + PERMISSION_FINE, listener); + + Location loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + verify(listener, timeout(TIMEOUT_MS).times(1)).onLocationChanged(locationCaptor.capture(), + nullable(IRemoteCallback.class)); + assertThat(locationCaptor.getValue()).isEqualTo(loc); + } + + @Test + public void testRegisterListener_Unregister() throws Exception { + ILocationListener listener = createMockLocationListener(); + mManager.registerLocationRequest( + LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), IDENTITY, + PERMISSION_FINE, listener); + mManager.unregisterLocationRequest(listener); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + verify(listener, never()).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + + mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER); + verify(listener, after(TIMEOUT_MS).never()).onProviderEnabledChanged(NAME, false); + } + + @Test + public void testRegisterListener_Unregister_SameProcess() throws Exception { + CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage", + "attribution"); + + ILocationListener listener = createMockLocationListener(); + mManager.registerLocationRequest( + LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity, + PERMISSION_FINE, listener); + + CountDownLatch blocker = new CountDownLatch(1); + ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> { + try { + blocker.await(); + } catch (InterruptedException e) { + // do nothing + } + }); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + mManager.unregisterLocationRequest(listener); + blocker.countDown(); + verify(listener, after(TIMEOUT_MS).never()).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + } + + @Test + public void testRegisterListener_NumUpdates() throws Exception { + ILocationListener listener = createMockLocationListener(); + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, + false).setNumUpdates(5); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + + verify(listener, times(5)).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + } + + @Test + public void testRegisterListener_ExpiringAlarm() throws Exception { + ILocationListener listener = createMockLocationListener(); + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, + false).setExpireIn(5000); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + long baseTimeMs = SystemClock.elapsedRealtime(); + + ArgumentCaptor<Long> timeoutCapture = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor<OnAlarmListener> listenerCapture = ArgumentCaptor.forClass( + OnAlarmListener.class); + verify(mAlarmManager).set(eq(ELAPSED_REALTIME_WAKEUP), timeoutCapture.capture(), + eq(WINDOW_EXACT), eq(0L), listenerCapture.capture(), any(Handler.class), + any(WorkSource.class)); + + assertThat(timeoutCapture.getValue()).isAtLeast(baseTimeMs + 4000); + assertThat(timeoutCapture.getValue()).isAtMost(baseTimeMs + 5000); + listenerCapture.getValue().onAlarm(); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + verify(listener, never()).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + } + + @Test + public void testRegisterListener_ExpiringNoAlarm() throws Exception { + ILocationListener listener = createMockLocationListener(); + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, + false).setExpireIn(25); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + + Thread.sleep(25); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + verify(listener, never()).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + } + + @Test + public void testRegisterListener_AlreadyExpired() throws Exception { + ILocationListener listener = createMockLocationListener(); + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, + false).setExpireIn(-1); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + verify(listener, never()).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + } + + @Test + public void testRegisterListener_FastestInterval() throws Exception { + ILocationListener listener = createMockLocationListener(); + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 5000, 0, + false).setFastestInterval(5000); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + + verify(listener, times(1)).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + } + + @Test + public void testRegisterListener_SmallestDisplacement() throws Exception { + ILocationListener listener = createMockLocationListener(); + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 5000, 0, + false).setSmallestDisplacement(1f); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + + Location loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + mProvider.setProviderLocation(loc); + + verify(listener, times(1)).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + } + + @Test + public void testRegisterListener_NoteOpFailure() throws Exception { + ILocationListener listener = createMockLocationListener(); + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + + mInjector.getAppOpsHelper().setAppOpAllowed(OP_FINE_LOCATION, IDENTITY.getPackageName(), + false); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + + verify(listener, never()).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + } + + @Test + public void testRegisterListener_Wakelock() throws Exception { + CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage", + "attribution"); + + ILocationListener listener = createMockLocationListener(); + mManager.registerLocationRequest( + LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity, + PERMISSION_FINE, listener); + + CountDownLatch blocker = new CountDownLatch(1); + ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> { + try { + blocker.await(); + } catch (InterruptedException e) { + // do nothing + } + }); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + verify(mWakeLock).acquire(anyLong()); + verify(mWakeLock, never()).release(); + + blocker.countDown(); + verify(listener, timeout(TIMEOUT_MS)).onLocationChanged(any(Location.class), + nullable(IRemoteCallback.class)); + verify(mWakeLock).acquire(anyLong()); + verify(mWakeLock, timeout(TIMEOUT_MS)).release(); + } + + @Test + public void testGetCurrentLocation() throws Exception { + ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class); + + ILocationCallback listener = createMockGetCurrentLocationListener(); + LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, + false); + ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); + mManager.getCurrentLocation(locationRequest, IDENTITY, + PERMISSION_FINE, cancellationSignal, listener); + + Location loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + + verify(listener, times(1)).onLocation(locationCaptor.capture()); + assertThat(locationCaptor.getValue()).isEqualTo(loc); + } + + @Test + public void testGetCurrentLocation_Cancel() throws Exception { + ILocationCallback listener = createMockGetCurrentLocationListener(); + LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, + false); + ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); + mManager.getCurrentLocation(locationRequest, IDENTITY, + PERMISSION_FINE, cancellationSignal, listener); + + cancellationSignal.cancel(); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + + verify(listener, never()).onLocation(nullable(Location.class)); + } + + @Test + public void testGetCurrentLocation_ProviderDisabled() throws Exception { + ILocationCallback listener = createMockGetCurrentLocationListener(); + LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, + false); + ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); + mManager.getCurrentLocation(locationRequest, IDENTITY, + PERMISSION_FINE, cancellationSignal, listener); + + mProvider.setProviderAllowed(false); + mProvider.setProviderAllowed(true); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + verify(listener, times(1)).onLocation(isNull()); + } + + @Test + public void testGetCurrentLocation_ProviderAlreadyDisabled() throws Exception { + mProvider.setProviderAllowed(false); + + ILocationCallback listener = createMockGetCurrentLocationListener(); + LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, + false); + ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); + mManager.getCurrentLocation(locationRequest, IDENTITY, + PERMISSION_FINE, cancellationSignal, listener); + + mProvider.setProviderAllowed(true); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + verify(listener, times(1)).onLocation(isNull()); + } + + @Test + public void testGetCurrentLocation_LastLocation() throws Exception { + ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class); + + Location loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + + ILocationCallback listener = createMockGetCurrentLocationListener(); + LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, + false); + ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); + mManager.getCurrentLocation(locationRequest, IDENTITY, + PERMISSION_FINE, cancellationSignal, listener); + + verify(listener, times(1)).onLocation(locationCaptor.capture()); + assertThat(locationCaptor.getValue()).isEqualTo(loc); + } + + @Test + public void testGetCurrentLocation_Timeout() throws Exception { + ILocationCallback listener = createMockGetCurrentLocationListener(); + LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, + false); + ICancellationSignal cancellationSignal = CancellationSignal.createTransport(); + mManager.getCurrentLocation(locationRequest, IDENTITY, + PERMISSION_FINE, cancellationSignal, listener); + + ArgumentCaptor<OnAlarmListener> listenerCapture = ArgumentCaptor.forClass( + OnAlarmListener.class); + verify(mAlarmManager).set(eq(ELAPSED_REALTIME_WAKEUP), anyLong(), + eq(WINDOW_EXACT), eq(0L), listenerCapture.capture(), any(Handler.class), + any(WorkSource.class)); + listenerCapture.getValue().onAlarm(); + + verify(listener, times(1)).onLocation(isNull()); + } + + @Test + public void testLocationMonitoring() { + assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION, + IDENTITY.getPackageName())).isFalse(); + assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION, + IDENTITY.getPackageName())).isFalse(); + + ILocationListener listener = createMockLocationListener(); + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + + assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION, + IDENTITY.getPackageName())).isTrue(); + assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION, + IDENTITY.getPackageName())).isTrue(); + + mInjector.getAppForegroundHelper().setAppForeground(IDENTITY.getUid(), false); + + assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION, + IDENTITY.getPackageName())).isTrue(); + assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION, + IDENTITY.getPackageName())).isFalse(); + + mManager.unregisterLocationRequest(listener); + + assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION, + IDENTITY.getPackageName())).isFalse(); + assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION, + IDENTITY.getPackageName())).isFalse(); + } + + @Test + public void testProviderRequest() { + assertThat(mProvider.getRequest().reportLocation).isFalse(); + assertThat(mProvider.getRequest().locationRequests).isEmpty(); + + ILocationListener listener1 = createMockLocationListener(); + LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false); + mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); + + assertThat(mProvider.getRequest().reportLocation).isTrue(); + assertThat(mProvider.getRequest().locationRequests).containsExactly(request1); + assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse(); + assertThat(mProvider.getRequest().interval).isEqualTo(5); + assertThat(mProvider.getRequest().lowPowerMode).isFalse(); + assertThat(mProvider.getRequest().workSource).isNotNull(); + + ILocationListener listener2 = createMockLocationListener(); + LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0, + false).setLowPowerMode(true); + mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); + + assertThat(mProvider.getRequest().reportLocation).isTrue(); + assertThat(mProvider.getRequest().locationRequests).containsExactly(request1, request2); + assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse(); + assertThat(mProvider.getRequest().interval).isEqualTo(1); + assertThat(mProvider.getRequest().lowPowerMode).isFalse(); + assertThat(mProvider.getRequest().workSource).isNotNull(); + + mManager.unregisterLocationRequest(listener1); + + assertThat(mProvider.getRequest().reportLocation).isTrue(); + assertThat(mProvider.getRequest().locationRequests).containsExactly(request2); + assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse(); + assertThat(mProvider.getRequest().interval).isEqualTo(1); + assertThat(mProvider.getRequest().lowPowerMode).isTrue(); + assertThat(mProvider.getRequest().workSource).isNotNull(); + + mManager.unregisterLocationRequest(listener2); + + assertThat(mProvider.getRequest().reportLocation).isFalse(); + assertThat(mProvider.getRequest().locationRequests).isEmpty(); + } + + @Test + public void testProviderRequest_BackgroundThrottle() { + ILocationListener listener1 = createMockLocationListener(); + LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false); + mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); + + assertThat(mProvider.getRequest().interval).isEqualTo(5); + + mInjector.getAppForegroundHelper().setAppForeground(IDENTITY.getUid(), false); + assertThat(mProvider.getRequest().interval).isEqualTo( + mInjector.getSettingsHelper().getBackgroundThrottleIntervalMs()); + } + + @Test + public void testProviderRequest_IgnoreLocationSettings() { + mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist( + Collections.singleton(IDENTITY.getPackageName())); + + ILocationListener listener1 = createMockLocationListener(); + LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false); + mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); + + assertThat(mProvider.getRequest().reportLocation).isTrue(); + assertThat(mProvider.getRequest().interval).isEqualTo(5); + assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse(); + + ILocationListener listener2 = createMockLocationListener(); + LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0, + false).setLocationSettingsIgnored(true); + mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); + + assertThat(mProvider.getRequest().reportLocation).isTrue(); + assertThat(mProvider.getRequest().interval).isEqualTo(1); + assertThat(mProvider.getRequest().locationSettingsIgnored).isTrue(); + } + + @Test + public void testProviderRequest_IgnoreLocationSettings_ProviderDisabled() { + mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist( + Collections.singleton(IDENTITY.getPackageName())); + + ILocationListener listener1 = createMockLocationListener(); + LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0, false); + mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); + + ILocationListener listener2 = createMockLocationListener(); + LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, + false).setLocationSettingsIgnored(true); + mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); + + mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId()); + + assertThat(mProvider.getRequest().reportLocation).isTrue(); + assertThat(mProvider.getRequest().locationRequests).containsExactly(request2); + assertThat(mProvider.getRequest().interval).isEqualTo(5); + assertThat(mProvider.getRequest().locationSettingsIgnored).isTrue(); + } + + @Test + public void testProviderRequest_IgnoreLocationSettings_NoAllowlist() { + mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist( + Collections.singleton(IDENTITY.getPackageName())); + + ILocationListener listener = createMockLocationListener(); + LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0, + false).setLocationSettingsIgnored(true); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + + mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(Collections.emptySet()); + + assertThat(mProvider.getRequest().reportLocation).isTrue(); + assertThat(mProvider.getRequest().interval).isEqualTo(1); + assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse(); + } + + @Test + public void testProviderRequest_BackgroundThrottle_IgnoreLocationSettings() { + mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist( + Collections.singleton(IDENTITY.getPackageName())); + + ILocationListener listener1 = createMockLocationListener(); + LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, + false).setLocationSettingsIgnored(true); + mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); + + assertThat(mProvider.getRequest().interval).isEqualTo(5); + + mInjector.getAppForegroundHelper().setAppForeground(IDENTITY.getUid(), false); + assertThat(mProvider.getRequest().interval).isEqualTo(5); + } + + private ILocationListener createMockLocationListener() { + return spy(new ILocationListener.Stub() { + @Override + public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) { + if (onCompleteCallback != null) { + try { + onCompleteCallback.sendResult(null); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + } + + @Override + public void onProviderEnabledChanged(String provider, boolean enabled) { + } + }); + } + + private ILocationCallback createMockGetCurrentLocationListener() { + return spy(new ILocationCallback.Stub() { + @Override + public void onLocation(Location location) { + } + }); + } + + private static class TestProvider extends AbstractLocationProvider { + + private ProviderRequest mProviderRequest = ProviderRequest.EMPTY_REQUEST; + + TestProvider(ProviderProperties properties, CallerIdentity identity) { + super(DIRECT_EXECUTOR, identity); + setProperties(properties); + } + + public void setProviderAllowed(boolean allowed) { + setAllowed(allowed); + } + + public void setProviderLocation(Location l) { + reportLocation(new Location(l)); + } + + public ProviderRequest getRequest() { + return mProviderRequest; + } + + @Override + public void onSetRequest(ProviderRequest request) { + mProviderRequest = request; + } + + @Override + protected void onExtraCommand(int uid, int pid, String command, Bundle extras) { + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java index c6922536f61a..1b6ac3c84210 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -183,13 +183,12 @@ public class VibratorServiceTest { @Test public void hasAmplitudeControl_withAmplitudeControlSupport_returnsTrue() { - when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); assertTrue(createService().hasAmplitudeControl()); } @Test public void hasAmplitudeControl_withNoAmplitudeControlSupport_returnsFalse() { - when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(false); assertFalse(createService().hasAmplitudeControl()); } @@ -270,7 +269,7 @@ public class VibratorServiceTest { @Test public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() { - when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorService service = createService(); Mockito.clearInvocations(mNativeWrapperMock); @@ -340,7 +339,7 @@ public class VibratorServiceTest { @Test public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime() throws Exception { - when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorService service = createService(); Mockito.clearInvocations(mNativeWrapperMock); @@ -511,7 +510,7 @@ public class VibratorServiceTest { setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); - when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorService service = createService(); service.systemReady(); diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index f991dff2797f..db56657e208c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -550,7 +550,6 @@ public class AppsFilterTest { .setAppId(DUMMY_TARGET_APPID) .setName("com.some.package") .setCodePath("/") - .setResourcePath("/") .setPVersionCode(1L) .build(); PackageSetting calling = simulateAddPackage(appsFilter, @@ -874,7 +873,6 @@ public class AppsFilterTest { .setAppId(appId) .setName(newPkg.getPackageName()) .setCodePath("/") - .setResourcePath("/") .setPVersionCode(1L); final PackageSetting setting = (action == null ? settingBuilder : action.withBuilder(settingBuilder)).build(); diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java index 164bd725dd66..90edaef4294f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java @@ -38,8 +38,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase { public PackageSetting generateFakePackageSetting(String name) { return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"), - new File(mContext.getCacheDir(), "fakeResPath"), "", "", "", - "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/, + "", "", "", "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index c4e25b53d5d5..80f145b16147 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -86,8 +86,7 @@ public class PackageManagerServiceTest { // Create a real (non-null) PackageSetting and confirm that the removed // users are copied properly setting = new PackageSetting("name", "realName", new File("codePath"), - new File("resourcePath"), "legacyNativeLibraryPathString", - "primaryCpuAbiString", "secondaryCpuAbiString", + "legacyNativeLibraryPathString", "primaryCpuAbiString", "secondaryCpuAbiString", "cpuAbiOverrideString", 0, 0, 0, 0, null, null, null); pri.populateUsers(new int[] { diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index aa92ba49d190..0bf06bb4dda7 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -439,7 +439,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME, REAL_PACKAGE_NAME, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -461,7 +460,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME /*pkgName*/, REAL_PACKAGE_NAME /*realPkgName*/, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -477,7 +475,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME /*pkgName*/, REAL_PACKAGE_NAME /*realPkgName*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, null /*primaryCpuAbiString*/, null /*secondaryCpuAbiString*/, @@ -507,7 +504,6 @@ public class PackageManagerSettingsTests { null /*disabledPkg*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -541,7 +537,6 @@ public class PackageManagerSettingsTests { null /*disabledPkg*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -581,7 +576,6 @@ public class PackageManagerSettingsTests { null /*disabledPkg*/, testUserSetting01 /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - null /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -609,7 +603,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -624,12 +617,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); - assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM)); assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)); assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); - assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi")); // signatures object must be different assertNotSame(testPkgSetting01.signatures, originalSignatures); @@ -649,7 +641,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, null /*sharedUser*/, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -665,12 +656,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); assertThat(testPkgSetting01.appId, is(0)); - assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64")); - assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86")); assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE)); // by default, the package is considered stopped @@ -695,7 +685,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, testUserSetting01 /*sharedUser*/, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -711,12 +700,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); assertThat(testPkgSetting01.appId, is(10064)); - assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64")); - assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86")); assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE)); final PackageUserState userState = testPkgSetting01.readUserState(0); @@ -738,7 +726,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -754,12 +741,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); assertThat(testPkgSetting01.appId, is(10064)); - assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); - assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi")); assertNotSame(testPkgSetting01.signatures, disabledSignatures); assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE)); @@ -806,10 +792,10 @@ public class PackageManagerSettingsTests { private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) { assertThat(origPkgSetting, is(not(testPkgSetting))); assertThat(origPkgSetting.appId, is(testPkgSetting.appId)); - assertSame(origPkgSetting.codePath, testPkgSetting.codePath); - assertThat(origPkgSetting.codePath, is(testPkgSetting.codePath)); - assertSame(origPkgSetting.codePathString, testPkgSetting.codePathString); - assertThat(origPkgSetting.codePathString, is(testPkgSetting.codePathString)); + assertSame(origPkgSetting.getCodePath(), testPkgSetting.getCodePath()); + assertThat(origPkgSetting.getCodePath(), is(testPkgSetting.getCodePath())); + assertSame(origPkgSetting.getCodePathString(), testPkgSetting.getCodePathString()); + assertThat(origPkgSetting.getCodePathString(), is(testPkgSetting.getCodePathString())); assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString); assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString)); assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime)); @@ -823,7 +809,9 @@ public class PackageManagerSettingsTests { testPkgSetting.legacyNativeLibraryPathString); assertThat(origPkgSetting.legacyNativeLibraryPathString, is(testPkgSetting.legacyNativeLibraryPathString)); - assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups); + if (origPkgSetting.mimeGroups != null) { + assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups); + } assertThat(origPkgSetting.mimeGroups, is(testPkgSetting.mimeGroups)); assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState); assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState)); @@ -839,10 +827,6 @@ public class PackageManagerSettingsTests { assertSame(origPkgSetting.primaryCpuAbiString, testPkgSetting.primaryCpuAbiString); assertThat(origPkgSetting.primaryCpuAbiString, is(testPkgSetting.primaryCpuAbiString)); assertThat(origPkgSetting.realName, is(testPkgSetting.realName)); - assertSame(origPkgSetting.resourcePath, testPkgSetting.resourcePath); - assertThat(origPkgSetting.resourcePath, is(testPkgSetting.resourcePath)); - assertSame(origPkgSetting.resourcePathString, testPkgSetting.resourcePathString); - assertThat(origPkgSetting.resourcePathString, is(testPkgSetting.resourcePathString)); assertSame(origPkgSetting.secondaryCpuAbiString, testPkgSetting.secondaryCpuAbiString); assertThat(origPkgSetting.secondaryCpuAbiString, is(testPkgSetting.secondaryCpuAbiString)); assertSame(origPkgSetting.sharedUser, testPkgSetting.sharedUser); @@ -874,7 +858,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME, REAL_PACKAGE_NAME, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -893,7 +876,6 @@ public class PackageManagerSettingsTests { packageName, packageName, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index b0b5386a49bd..2651cfa5449b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -312,8 +312,7 @@ public class PackageParserTest { private static PackageSetting mockPkgSetting(AndroidPackage pkg) { return new PackageSetting(pkg.getPackageName(), pkg.getRealPackage(), - new File(pkg.getCodePath()), new File(pkg.getCodePath()), null, - pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(), + new File(pkg.getCodePath()), null, pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(), null, pkg.getVersionCode(), PackageInfoUtils.appInfoFlags(pkg, null), PackageInfoUtils.appInfoPrivateFlags(pkg, null), diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java index d12ea89469b7..f8e92ad71d8e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java @@ -30,7 +30,6 @@ public class PackageSettingBuilder { private String mName; private String mRealName; private String mCodePath; - private String mResourcePath; private String mLegacyNativeLibraryPathString; private String mPrimaryCpuAbiString; private String mSecondaryCpuAbiString; @@ -74,11 +73,6 @@ public class PackageSettingBuilder { return this; } - public PackageSettingBuilder setResourcePath(String resourcePath) { - this.mResourcePath = resourcePath; - return this; - } - public PackageSettingBuilder setLegacyNativeLibraryPathString( String legacyNativeLibraryPathString) { this.mLegacyNativeLibraryPathString = legacyNativeLibraryPathString; @@ -162,10 +156,10 @@ public class PackageSettingBuilder { public PackageSetting build() { final PackageSetting packageSetting = new PackageSetting(mName, mRealName, - new File(mCodePath), new File(mResourcePath), - mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString, - mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mSharedUserId, - mUsesStaticLibraries, mUsesStaticLibrariesVersions, mMimeGroups); + new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, + mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags, + mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions, + mMimeGroups); packageSetting.signatures = mSigningDetails != null ? new PackageSignatures(mSigningDetails) : new PackageSignatures(); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java index 55bc6812328a..7108490f80f8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java @@ -465,9 +465,9 @@ public class PackageSignaturesTest { // Generic PackageSetting object with values from a test app installed on a device to be // used to test the methods under the PackageSignatures signatures data member. File appPath = new File("/data/app/app"); - PackageSetting result = new PackageSetting("test.app", null, appPath, appPath, - "/data/app/app", null, null, null, - 1, 940097092, 0, 0 /*userId*/, null, null, null /*mimeGroups*/); + PackageSetting result = new PackageSetting("test.app", null, appPath, + "/data/app/app", null, null, null, 1, 940097092, 0, 0 /*userId*/, null, null, + null /*mimeGroups*/); return result; } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index e7eff00c472e..56dddb0a8112 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -484,8 +484,7 @@ public class ScanTests { private static PackageSettingBuilder createBasicPackageSettingBuilder(String packageName) { return new PackageSettingBuilder() .setName(packageName) - .setCodePath(createCodePath(packageName)) - .setResourcePath(createCodePath(packageName)); + .setCodePath(createCodePath(packageName)); } private static ScanRequestBuilder createBasicScanRequestBuilder(ParsingPackage pkg) { @@ -534,8 +533,7 @@ public class ScanTests { arrayContaining("some.static.library", "some.other.static.library")); assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L})); assertThat(pkgSetting.pkg, is(scanResult.request.parsedPackage)); - assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName)))); - assertThat(pkgSetting.resourcePath, is(new File(createCodePath(packageName)))); + assertThat(pkgSetting.getCodePath(), is(new File(createCodePath(packageName)))); assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345))); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java index 153634548c17..8034cacc6923 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java @@ -170,7 +170,7 @@ public class TimeZoneDetectorServiceTest { ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class); try { mTimeZoneDetectorService.removeConfigurationListener(mockListener); - fail(); + fail("Expected a SecurityException"); } finally { verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java index 1d75967756c3..88b1d191dc4d 100644 --- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java @@ -16,6 +16,7 @@ package com.android.server; +import android.Manifest; import android.app.AlarmManager; import android.app.IUiModeManager; import android.content.BroadcastReceiver; @@ -24,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Handler; @@ -67,6 +69,7 @@ import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -230,6 +233,17 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { } @Test + public void setNightModeActivated_permissiontoChangeOtherUsers() throws RemoteException { + SystemService.TargetUser user = mock(SystemService.TargetUser.class); + doReturn(9).when(user).getUserIdentifier(); + mUiManagerService.onUserSwitching(user, user); + when(mContext.checkCallingOrSelfPermission( + eq(Manifest.permission.INTERACT_ACROSS_USERS))) + .thenReturn(PackageManager.PERMISSION_DENIED); + assertFalse(mService.setNightModeActivated(true)); + } + + @Test public void autoNightModeSwitch_batterySaverOn() throws RemoteException { mService.setNightMode(MODE_NIGHT_NO); when(mTwilightState.isNight()).thenReturn(false); diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 4040fa6a675e..e3c795d03381 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -38,6 +38,7 @@ <uses-permission android:name="android.permission.REORDER_TASKS" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.STATUS_BAR" /> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" /> <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) --> <application android:debuggable="true" diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java index addf1ffe40c2..96b970056c98 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -38,7 +38,13 @@ import static org.mockito.ArgumentMatchers.eq; import android.app.WaitResult; import android.content.pm.ActivityInfo; +import android.graphics.PixelFormat; +import android.hardware.display.DisplayManager; +import android.hardware.display.VirtualDisplay; +import android.media.ImageReader; import android.platform.test.annotations.Presubmit; +import android.view.Display; +import android.view.DisplayInfo; import androidx.test.filters.MediumTest; @@ -173,4 +179,50 @@ public class ActivityStackSupervisorTests extends WindowTestsBase { verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskB.mTaskId) /* taskId */, eq(true) /* focused */); } + + @Test + /** Ensures that a trusted virtual display can launch arbitrary activities. */ + public void testTrustedVirtualDisplayCanLaunchActivities() { + final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP); + final Task stack = new StackBuilder(mRootWindowContainer) + .setDisplay(newDisplay).build(); + final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity(); + VirtualDisplay virtualDisplay = createVirtualDisplay(true); + final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234, + virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info); + + assertThat(allowed).isTrue(); + } + + @Test + /** Ensures that an untrusted virtual display cannot launch arbitrary activities. */ + public void testUntrustedVirtualDisplayCannotLaunchActivities() { + final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP); + final Task stack = new StackBuilder(mRootWindowContainer) + .setDisplay(newDisplay).build(); + final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity(); + VirtualDisplay virtualDisplay = createVirtualDisplay(false); + final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234, + virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info); + + assertThat(allowed).isFalse(); + } + + private VirtualDisplay createVirtualDisplay(boolean trusted) { + final DisplayManager dm = mContext.getSystemService(DisplayManager.class); + final DisplayInfo displayInfo = new DisplayInfo(); + final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY); + defaultDisplay.getDisplayInfo(displayInfo); + int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; + if (trusted) { + flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; + } + + final ImageReader imageReader = ImageReader.newInstance( + displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2); + + return dm.createVirtualDisplay("virtualDisplay", displayInfo.logicalWidth, + displayInfo.logicalHeight, + displayInfo.logicalDensityDpi, imageReader.getSurface(), flags); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index a137cde2d351..044f81986517 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -168,8 +168,8 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_once() throws Exception { - // GIVEN a task record with whitelisted auth - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN a task record with allowlisted auth + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); // WHEN calling setLockTaskMode for LOCKED mode without resuming mLockTaskController.startLockTaskMode(tr, false, TEST_UID); @@ -185,9 +185,9 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_twice() throws Exception { - // GIVEN two task records with whitelisted auth - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); // WHEN calling setLockTaskMode for LOCKED mode on both tasks mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); @@ -205,7 +205,7 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_pinningRequest() { - // GIVEN a task record that is not whitelisted, i.e. with pinned auth + // GIVEN a task record that is not allowlisted, i.e. with pinned auth Task tr = getTask(Task.LOCK_TASK_AUTH_PINNABLE); // WHEN calling startLockTaskMode @@ -236,23 +236,23 @@ public class LockTaskControllerTest { @Test public void testLockTaskViolation() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN it's not a lock task violation to try and launch this task without clearing assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false)); - // THEN it's a lock task violation to launch another task that is not whitelisted + // THEN it's a lock task violation to launch another task that is not allowlisted assertTrue(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_PINNABLE))); // THEN it's a lock task violation to launch another task that is disallowed from lock task assertTrue(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_DONT_LOCK))); - // THEN it's no a lock task violation to launch another task that is whitelisted + // THEN it's no a lock task violation to launch another task that is allowlisted assertFalse(mLockTaskController.isLockTaskModeViolation(getTask( - Task.LOCK_TASK_AUTH_WHITELISTED))); + Task.LOCK_TASK_AUTH_ALLOWLISTED))); assertFalse(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_LAUNCHABLE))); // THEN it's not a lock task violation to launch another task that is priv launchable @@ -262,8 +262,8 @@ public class LockTaskControllerTest { @Test public void testLockTaskViolation_emergencyCall() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // GIVEN tasks necessary for emergency calling @@ -294,8 +294,8 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode() throws Exception { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN the same caller calls stopLockTaskMode @@ -311,8 +311,8 @@ public class LockTaskControllerTest { @Test(expected = SecurityException.class) public void testStopLockTaskMode_differentCaller() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN a different caller calls stopLockTaskMode @@ -323,8 +323,8 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_systemCaller() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN system calls stopLockTaskMode @@ -336,9 +336,9 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_twoTasks() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -357,9 +357,9 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_rootTask() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -405,9 +405,9 @@ public class LockTaskControllerTest { @Test public void testClearLockedTasks() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -434,7 +434,7 @@ public class LockTaskControllerTest { .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -454,7 +454,7 @@ public class LockTaskControllerTest { .thenReturn(true); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -471,7 +471,7 @@ public class LockTaskControllerTest { Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1, mContext.getUserId()); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -488,7 +488,7 @@ public class LockTaskControllerTest { Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 0, mContext.getUserId()); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -500,45 +500,45 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskPackages() { - String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; - String[] whitelist2 = {TEST_PACKAGE_NAME}; - - // No package is whitelisted initially - for (String pkg : whitelist1) { - assertFalse("Package shouldn't be whitelisted: " + pkg, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg)); - assertFalse("Package shouldn't be whitelisted for user 0: " + pkg, - mLockTaskController.isPackageWhitelisted(0, pkg)); + String[] allowlist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; + String[] allowlist2 = {TEST_PACKAGE_NAME}; + + // No package is allowlisted initially + for (String pkg : allowlist1) { + assertFalse("Package shouldn't be allowlisted: " + pkg, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg)); + assertFalse("Package shouldn't be allowlisted for user 0: " + pkg, + mLockTaskController.isPackageAllowlisted(0, pkg)); } - // Apply whitelist - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist1); + // Apply allowlist + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist1); - // Assert the whitelist is applied to the correct user - for (String pkg : whitelist1) { - assertTrue("Package should be whitelisted: " + pkg, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg)); - assertFalse("Package shouldn't be whitelisted for user 0: " + pkg, - mLockTaskController.isPackageWhitelisted(0, pkg)); + // Assert the allowlist is applied to the correct user + for (String pkg : allowlist1) { + assertTrue("Package should be allowlisted: " + pkg, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg)); + assertFalse("Package shouldn't be allowlisted for user 0: " + pkg, + mLockTaskController.isPackageAllowlisted(0, pkg)); } - // Update whitelist - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist2); + // Update allowlist + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist2); - // Assert the new whitelist is applied - assertTrue("Package should remain whitelisted: " + TEST_PACKAGE_NAME, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME)); - assertFalse("Package should no longer be whitelisted: " + TEST_PACKAGE_NAME_2, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME_2)); + // Assert the new allowlist is applied + assertTrue("Package should remain allowlisted: " + TEST_PACKAGE_NAME, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME)); + assertFalse("Package should no longer be allowlisted: " + TEST_PACKAGE_NAME_2, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME_2)); } @Test public void testUpdateLockTaskPackages_taskRemoved() throws Exception { - // GIVEN two tasks which are whitelisted initially + // GIVEN two tasks which are allowlisted initially Task tr1 = getTaskForUpdate(TEST_PACKAGE_NAME, true); Task tr2 = getTaskForUpdate(TEST_PACKAGE_NAME_2, false); - String[] whitelist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + String[] allowlist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // GIVEN the tasks are launched into LockTask mode mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); @@ -548,9 +548,9 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isTaskLocked(tr2)); verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); - // WHEN removing one package from whitelist - whitelist = new String[] {TEST_PACKAGE_NAME}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // WHEN removing one package from allowlist + allowlist = new String[] {TEST_PACKAGE_NAME}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // THEN the task running that package should be stopped verify(tr2).performClearTaskLocked(); @@ -560,9 +560,9 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isTaskLocked(tr1)); verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); - // WHEN removing the last package from whitelist - whitelist = new String[] {}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // WHEN removing the last package from allowlist + allowlist = new String[] {}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // THEN the last task should be cleared, and the system should quit LockTask mode verify(tr1).performClearTaskLocked(); @@ -574,7 +574,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures() throws Exception { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN lock task mode should be started with default status bar masks @@ -616,7 +616,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures_differentUser() throws Exception { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN lock task mode should be started with default status bar masks @@ -638,7 +638,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures_keyguard() { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN keyguard should be disabled @@ -704,7 +704,7 @@ public class LockTaskControllerTest { TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); // Start lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is not enabled @@ -719,15 +719,15 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS)); - // unwhitelisted package should not be allowed + // unallowlisted package should not be allowed assertFalse(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); - // update the whitelist - String[] whitelist = new String[] { TEST_PACKAGE_NAME }; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // update the allowlist + String[] allowlist = new String[] { TEST_PACKAGE_NAME }; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); - // whitelisted package should be allowed + // allowlisted package should be allowed assertTrue(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); @@ -755,17 +755,17 @@ public class LockTaskControllerTest { } /** - * @param isAppAware {@code true} if the app has marked if_whitelisted in its manifest + * @param isAppAware {@code true} if the app has marked if allowlisted in its manifest */ private Task getTaskForUpdate(String pkg, boolean isAppAware) { - final int authIfWhitelisted = isAppAware + final int authIfAllowlisted = isAppAware ? Task.LOCK_TASK_AUTH_LAUNCHABLE - : Task.LOCK_TASK_AUTH_WHITELISTED; - Task tr = getTask(pkg, authIfWhitelisted); + : Task.LOCK_TASK_AUTH_ALLOWLISTED; + Task tr = getTask(pkg, authIfAllowlisted); doAnswer((invocation) -> { - boolean isWhitelisted = - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg); - tr.mLockTaskAuth = isWhitelisted ? authIfWhitelisted : Task.LOCK_TASK_AUTH_PINNABLE; + boolean isAllowlisted = + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg); + tr.mLockTaskAuth = isAllowlisted ? authIfAllowlisted : Task.LOCK_TASK_AUTH_PINNABLE; return null; }).when(tr).setLockTaskAuth(); return tr; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index d950344538a0..b4a13375aeec 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; @@ -87,7 +88,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { 0 /* systemUiVisibility */, false /* isTranslucent */); mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test", createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0, - taskBounds, ORIENTATION_PORTRAIT, new InsetsState()); + taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, new InsetsState()); } private static TaskDescription createTaskDescription(int background, int statusBar, diff --git a/startop/OWNERS b/startop/OWNERS index 3394be9779e3..2d1eb38952ed 100644 --- a/startop/OWNERS +++ b/startop/OWNERS @@ -1,7 +1,7 @@ # mailing list: startop-eng@google.com +calin@google.com chriswailes@google.com eholk@google.com iam@google.com mathieuc@google.com -sehr@google.com yawanng@google.com diff --git a/test-runner/src/android/test/AndroidTestRunner.java b/test-runner/src/android/test/AndroidTestRunner.java index f898516a001b..b2fdc5084834 100644 --- a/test-runner/src/android/test/AndroidTestRunner.java +++ b/test-runner/src/android/test/AndroidTestRunner.java @@ -125,7 +125,7 @@ public class AndroidTestRunner extends BaseTestRunner { } catch (IllegalArgumentException e) { runFailed("Illegal argument passed to constructor. Class: " + testClass.getName()); } catch (InvocationTargetException e) { - runFailed("Constructor thew an exception. Class: " + testClass.getName()); + runFailed("Constructor threw an exception. Class: " + testClass.getName()); } return null; } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt index 2e4d390ceb60..c0658fe4422e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt @@ -84,7 +84,7 @@ class CloseImeAutoOpenWindowToHomeTest( navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible(bugId = 140855415) noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false) - navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0) + navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415) statusBarLayerRotatesScales(rotation, Surface.ROTATION_0) imeLayerBecomesInvisible(bugId = 141458352) imeAppLayerBecomesInvisible(testApp, bugId = 153739621) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt index 1c0da4f920bb..dcf308533ee6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt @@ -92,7 +92,7 @@ open class CloseImeWindowToHomeTest( navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible(bugId = 140855415) noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false) - navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0) + navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415) statusBarLayerRotatesScales(rotation, Surface.ROTATION_0) imeLayerBecomesInvisible(bugId = 153739621) imeAppLayerBecomesInvisible(testApp, bugId = 153739621) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt index e078f266e5ed..91ec211805f7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt @@ -90,7 +90,7 @@ class OpenAppToSplitScreenTest( navBarLayerIsAlwaysVisible() statusBarLayerIsAlwaysVisible() noUncoveredRegions(rotation) - navBarLayerRotatesAndScales(rotation) + navBarLayerRotatesAndScales(rotation, bugId = 140855415) statusBarLayerRotatesScales(rotation) all("dividerLayerBecomesVisible") { @@ -102,7 +102,8 @@ class OpenAppToSplitScreenTest( eventLog { focusChanges(testApp.`package`, - "recents_animation_input_consumer", "NexusLauncherActivity") + "recents_animation_input_consumer", "NexusLauncherActivity", + bugId = 151179149) } } } diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java index 51bae3af3e9c..08144c845555 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java @@ -60,7 +60,7 @@ public class ColorFiltersMutateActivity extends Activity { static final String sSkSL = "uniform float param1;\n" - + "void main(float x, float y, inout half4 color) {\n" + + "void main(float2 xy, inout half4 color) {\n" + "color = half4(color.r, half(param1), color.b, 1.0);\n" + "}\n"; diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp new file mode 100644 index 000000000000..9d35cbc3de7f --- /dev/null +++ b/tests/Input/Android.bp @@ -0,0 +1,12 @@ +android_test { + name: "InputTests", + srcs: ["src/**/*.kt"], + platform_apis: true, + certificate: "platform", + static_libs: [ + "androidx.test.ext.junit", + "androidx.test.rules", + "android-support-test", + "ub-uiautomator", + ], +} diff --git a/tests/Input/AndroidManifest.xml b/tests/Input/AndroidManifest.xml new file mode 100644 index 000000000000..4195df72864c --- /dev/null +++ b/tests/Input/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.input"> + <uses-permission android:name="android.permission.MONITOR_INPUT"/> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> + <uses-permission android:name="android.permission.INJECT_EVENTS"/> + + <application android:label="InputTest"> + + <activity android:name=".UnresponsiveGestureMonitorActivity" + android:label="Unresponsive gesture monitor" + android:process=":externalProcess"> + </activity> + + + </application> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.test.input" + android:label="Input Tests"/> +</manifest> diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml new file mode 100644 index 000000000000..c62db1ea5ca9 --- /dev/null +++ b/tests/Input/AndroidTest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright 2020 Google Inc. All Rights Reserved. + --> +<configuration description="Runs Input Tests"> + <option name="test-tag" value="InputTests" /> + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on" /> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="InputTests.apk"/> + + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.test.input"/> + <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" /> + <option name="shell-timeout" value="660s" /> + <option name="test-timeout" value="600s" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + </test> +</configuration> diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt new file mode 100644 index 000000000000..4da3eca25ea0 --- /dev/null +++ b/tests/Input/src/com/android/test/input/AnrTest.kt @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 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.test.input + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.filters.MediumTest + +import android.graphics.Rect +import android.os.SystemClock +import android.provider.Settings +import android.provider.Settings.Global.HIDE_ERROR_DIALOGS +import android.support.test.uiautomator.By +import android.support.test.uiautomator.UiDevice +import android.support.test.uiautomator.UiObject2 +import android.support.test.uiautomator.Until +import android.view.InputDevice +import android.view.MotionEvent + +import org.junit.After +import org.junit.Assert.fail +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** + * This test makes sure that an unresponsive gesture monitor gets an ANR. + * + * The gesture monitor must be registered from a different process than the instrumented process. + * Otherwise, when the test runs, you will get: + * Test failed to run to completion. + * Reason: 'Instrumentation run failed due to 'keyDispatchingTimedOut''. + * Check device logcat for details + * RUNNER ERROR: Instrumentation run failed due to 'keyDispatchingTimedOut' + */ +@MediumTest +@RunWith(AndroidJUnit4::class) +class AnrTest { + companion object { + private const val TAG = "AnrTest" + } + + val mInstrumentation = InstrumentationRegistry.getInstrumentation() + var mHideErrorDialogs = 0 + + @Before + fun setUp() { + val contentResolver = mInstrumentation.targetContext.contentResolver + mHideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0) + Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, 0) + } + + @After + fun tearDown() { + val contentResolver = mInstrumentation.targetContext.contentResolver + Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, mHideErrorDialogs) + } + + @Test + fun testGestureMonitorAnr() { + startUnresponsiveActivity() + val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation) + val obj: UiObject2? = uiDevice.wait(Until.findObject( + By.text("Unresponsive gesture monitor")), 10000) + + if (obj == null) { + fail("Could not find unresponsive activity") + return + } + + val rect: Rect = obj.visibleBounds + val downTime = SystemClock.uptimeMillis() + val downEvent = MotionEvent.obtain(downTime, downTime, + MotionEvent.ACTION_DOWN, rect.left.toFloat(), rect.top.toFloat(), 0 /* metaState */) + downEvent.source = InputDevice.SOURCE_TOUCHSCREEN + + mInstrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/) + + // Todo: replace using timeout from android.hardware.input.IInputManager + SystemClock.sleep(5000) // default ANR timeout for gesture monitors + + clickCloseAppOnAnrDialog() + } + + private fun clickCloseAppOnAnrDialog() { + // Find anr dialog and kill app + val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation) + val closeAppButton: UiObject2? = + uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000) + if (closeAppButton == null) { + fail("Could not find anr dialog") + return + } + closeAppButton.click() + } + + private fun startUnresponsiveActivity() { + val flags = " -W -n " + val startCmd = "am start $flags com.android.test.input/.UnresponsiveGestureMonitorActivity" + mInstrumentation.uiAutomation.executeShellCommand(startCmd) + } +}
\ No newline at end of file diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt new file mode 100644 index 000000000000..d83a4570fedc --- /dev/null +++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2020 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.test.input + +import android.app.Activity +import android.hardware.input.InputManager +import android.os.Bundle +import android.os.Looper +import android.util.Log +import android.view.InputChannel +import android.view.InputEvent +import android.view.InputEventReceiver +import android.view.InputMonitor + +class UnresponsiveReceiver(channel: InputChannel, looper: Looper) : + InputEventReceiver(channel, looper) { + companion object { + const val TAG = "UnresponsiveReceiver" + } + override fun onInputEvent(event: InputEvent) { + Log.i(TAG, "Received $event") + // Not calling 'finishInputEvent' in order to trigger the ANR + } +} + +class UnresponsiveGestureMonitorActivity : Activity() { + companion object { + const val MONITOR_NAME = "unresponsive gesture monitor" + } + private lateinit var mInputEventReceiver: InputEventReceiver + private lateinit var mInputMonitor: InputMonitor + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mInputMonitor = InputManager.getInstance().monitorGestureInput(MONITOR_NAME, displayId) + mInputEventReceiver = UnresponsiveReceiver( + mInputMonitor.getInputChannel(), Looper.myLooper()) + } +} diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index da6018e2e2c9..530d0e492f2e 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -29,6 +29,7 @@ java_test_host { "compatibility-tradefed", "frameworks-base-hostutils", "module_test_util", + "cts-install-lib-host", ], data: [ ":com.android.apex.cts.shim.v2_prebuilt", diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index 7cfbdc2b5062..5285b04f67d7 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; +import com.android.cts.install.lib.host.InstallUtilsHost; import com.android.ddmlib.Log; import com.android.tests.rollback.host.AbandonSessionsRule; import com.android.tests.util.ModuleTestUtils; @@ -49,6 +50,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { private static final String APK_A = "TestAppAv1.apk"; private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this); + private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); /** * Runs the given phase of a test by calling into the device. @@ -93,7 +95,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @Test public void testAdbStagedInstallWaitForReadyFlagWorks() throws Exception { assumeTrue("Device does not support updating APEX", - mTestUtils.isApexUpdateSupported()); + mHostUtils.isApexUpdateSupported()); File apexFile = mTestUtils.getTestFile(SHIM_V2); String output = getDevice().executeAdbCommand("install", "--staged", @@ -107,7 +109,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @Test public void testAdbStagedInstallNoWaitFlagWorks() throws Exception { assumeTrue("Device does not support updating APEX", - mTestUtils.isApexUpdateSupported()); + mHostUtils.isApexUpdateSupported()); File apexFile = mTestUtils.getTestFile(SHIM_V2); String output = getDevice().executeAdbCommand("install", "--staged", @@ -122,7 +124,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @Test public void testAdbInstallMultiPackageCommandWorks() throws Exception { assumeTrue("Device does not support updating APEX", - mTestUtils.isApexUpdateSupported()); + mHostUtils.isApexUpdateSupported()); File apexFile = mTestUtils.getTestFile(SHIM_V2); File apkFile = mTestUtils.getTestFile(APK_A); diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 4ccf79a0cb37..de1c5759ee87 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -30,6 +30,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -49,6 +50,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.NotificationManager; @@ -65,6 +67,7 @@ import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpSecManager; import android.net.LinkProperties; +import android.net.LocalSocket; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo.DetailedState; @@ -74,6 +77,7 @@ import android.net.VpnManager; import android.net.VpnService; import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.os.ConditionVariable; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Process; @@ -101,13 +105,20 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.net.Inet4Address; +import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.stream.Stream; /** @@ -133,7 +144,8 @@ public class VpnTest { managedProfileA.profileGroupId = primaryUser.id; } - static final String TEST_VPN_PKG = "com.dummy.vpn"; + static final String EGRESS_IFACE = "wlan0"; + static final String TEST_VPN_PKG = "com.testvpn.vpn"; private static final String TEST_VPN_SERVER = "1.2.3.4"; private static final String TEST_VPN_IDENTITY = "identity"; private static final byte[] TEST_VPN_PSK = "psk".getBytes(); @@ -1012,31 +1024,190 @@ public class VpnTest { // a subsequent CL. } - @Test - public void testStartLegacyVpn() throws Exception { + public Vpn startLegacyVpn(final VpnProfile vpnProfile) throws Exception { final Vpn vpn = createVpn(primaryUser.id); setMockedUsers(primaryUser); // Dummy egress interface - final String egressIface = "DUMMY0"; final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(egressIface); + lp.setInterfaceName(EGRESS_IFACE); final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), - InetAddresses.parseNumericAddress("192.0.2.0"), egressIface); + InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE); lp.addRoute(defaultRoute); - vpn.startLegacyVpn(mVpnProfile, mKeyStore, lp); + vpn.startLegacyVpn(vpnProfile, mKeyStore, lp); + return vpn; + } + @Test + public void testStartPlatformVpn() throws Exception { + startLegacyVpn(mVpnProfile); // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in - // a subsequent CL. + // a subsequent patch. + } + + @Test + public void testStartRacoonNumericAddress() throws Exception { + startRacoon("1.2.3.4", "1.2.3.4"); + } + + @Test + public void testStartRacoonHostname() throws Exception { + startRacoon("hostname", "5.6.7.8"); // address returned by deps.resolve + } + + public void startRacoon(final String serverAddr, final String expectedAddr) + throws Exception { + final ConditionVariable legacyRunnerReady = new ConditionVariable(); + final VpnProfile profile = new VpnProfile("testProfile" /* key */); + profile.type = VpnProfile.TYPE_L2TP_IPSEC_PSK; + profile.name = "testProfileName"; + profile.username = "userName"; + profile.password = "thePassword"; + profile.server = serverAddr; + profile.ipsecIdentifier = "id"; + profile.ipsecSecret = "secret"; + profile.l2tpSecret = "l2tpsecret"; + when(mConnectivityManager.getAllNetworks()) + .thenReturn(new Network[] { new Network(101) }); + when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(), + anyInt(), any(), anyInt())).thenAnswer(invocation -> { + // The runner has registered an agent and is now ready. + legacyRunnerReady.open(); + return new Network(102); + }); + final Vpn vpn = startLegacyVpn(profile); + final TestDeps deps = (TestDeps) vpn.mDeps; + try { + // udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK + assertArrayEquals( + new String[] { EGRESS_IFACE, expectedAddr, "udppsk", + profile.ipsecIdentifier, profile.ipsecSecret, "1701" }, + deps.racoonArgs.get(10, TimeUnit.SECONDS)); + // literal values are hardcoded in Vpn.java for mtpd args + assertArrayEquals( + new String[] { EGRESS_IFACE, "l2tp", expectedAddr, "1701", profile.l2tpSecret, + "name", profile.username, "password", profile.password, + "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns", + "idle", "1800", "mtu", "1400", "mru", "1400" }, + deps.mtpdArgs.get(10, TimeUnit.SECONDS)); + // Now wait for the runner to be ready before testing for the route. + legacyRunnerReady.block(10_000); + // In this test the expected address is always v4 so /32 + final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"), + RouteInfo.RTN_THROW); + assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : " + + vpn.mConfig.routes, + vpn.mConfig.routes.contains(expectedRoute)); + } finally { + // Now interrupt the thread, unblock the runner and clean up. + vpn.mVpnRunner.exitVpnRunner(); + deps.getStateFile().delete(); // set to delete on exit, but this deletes it earlier + vpn.mVpnRunner.join(10_000); // wait for up to 10s for the runner to die and cleanup + } + } + + private static final class TestDeps extends Vpn.Dependencies { + public final CompletableFuture<String[]> racoonArgs = new CompletableFuture(); + public final CompletableFuture<String[]> mtpdArgs = new CompletableFuture(); + public final File mStateFile; + + private final HashMap<String, Boolean> mRunningServices = new HashMap<>(); + + TestDeps() { + try { + mStateFile = File.createTempFile("vpnTest", ".tmp"); + mStateFile.deleteOnExit(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void startService(final String serviceName) { + mRunningServices.put(serviceName, true); + } + + @Override + public void stopService(final String serviceName) { + mRunningServices.put(serviceName, false); + } + + @Override + public boolean isServiceRunning(final String serviceName) { + return mRunningServices.getOrDefault(serviceName, false); + } + + @Override + public boolean isServiceStopped(final String serviceName) { + return !isServiceRunning(serviceName); + } + + @Override + public File getStateFile() { + return mStateFile; + } + + @Override + public void sendArgumentsToDaemon( + final String daemon, final LocalSocket socket, final String[] arguments, + final Vpn.RetryScheduler interruptChecker) throws IOException { + if ("racoon".equals(daemon)) { + racoonArgs.complete(arguments); + } else if ("mtpd".equals(daemon)) { + writeStateFile(arguments); + mtpdArgs.complete(arguments); + } else { + throw new UnsupportedOperationException("Unsupported daemon : " + daemon); + } + } + + private void writeStateFile(final String[] arguments) throws IOException { + mStateFile.delete(); + mStateFile.createNewFile(); + mStateFile.deleteOnExit(); + final BufferedWriter writer = new BufferedWriter( + new FileWriter(mStateFile, false /* append */)); + writer.write(EGRESS_IFACE); + writer.write("\n"); + // addresses + writer.write("10.0.0.1/24\n"); + // routes + writer.write("192.168.6.0/24\n"); + // dns servers + writer.write("192.168.6.1\n"); + // search domains + writer.write("vpn.searchdomains.com\n"); + // endpoint - intentionally empty + writer.write("\n"); + writer.flush(); + writer.close(); + } + + @Override + @NonNull + public InetAddress resolve(final String endpoint) { + try { + // If a numeric IP address, return it. + return InetAddress.parseNumericAddress(endpoint); + } catch (IllegalArgumentException e) { + // Otherwise, return some token IP to test for. + return InetAddress.parseNumericAddress("5.6.7.8"); + } + } + + @Override + public boolean checkInterfacePresent(final Vpn vpn, final String iface) { + return true; + } } /** * Mock some methods of vpn object. */ private Vpn createVpn(@UserIdInt int userId) { - return new Vpn(Looper.myLooper(), mContext, mNetService, + return new Vpn(Looper.myLooper(), mContext, new TestDeps(), mNetService, userId, mKeyStore, mSystemServices, mIkev2SessionCreator); } diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java index f30c35aca8da..c2a5459ae125 100644 --- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java +++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java @@ -34,6 +34,8 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import javax.annotation.Nullable; @@ -49,7 +51,7 @@ import javax.annotation.Nullable; public class SystemPreparer extends ExternalResource { private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000; - // The paths of the files pushed onto the device through this rule. + // The paths of the files pushed onto the device through this rule to be removed after. private ArrayList<String> mPushedFiles = new ArrayList<>(); // The package names of packages installed through this rule. @@ -81,7 +83,7 @@ public class SystemPreparer extends ExternalResource { final ITestDevice device = mDeviceProvider.getDevice(); remount(); assertTrue(device.pushFile(copyResourceToTemp(filePath), outputPath)); - mPushedFiles.add(outputPath); + addPushedFile(device, outputPath); return this; } @@ -91,10 +93,23 @@ public class SystemPreparer extends ExternalResource { final ITestDevice device = mDeviceProvider.getDevice(); remount(); assertTrue(device.pushFile(file, outputPath)); - mPushedFiles.add(outputPath); + addPushedFile(device, outputPath); return this; } + private void addPushedFile(ITestDevice device, String outputPath) + throws DeviceNotAvailableException { + Path pathCreated = Paths.get(outputPath); + + // Find the top most parent that is new to the device + while (pathCreated.getParent() != null + && !device.doesFileExist(pathCreated.getParent().toString())) { + pathCreated = pathCreated.getParent(); + } + + mPushedFiles.add(pathCreated.toString()); + } + /** Deletes the given path from the device */ public SystemPreparer deleteFile(String file) throws DeviceNotAvailableException { final ITestDevice device = mDeviceProvider.getDevice(); @@ -203,7 +218,7 @@ public class SystemPreparer extends ExternalResource { /** Removes installed packages and files that were pushed to the device. */ @Override - protected void after() { + public void after() { final ITestDevice device = mDeviceProvider.getDevice(); try { remount(); diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 3cdfb00d288c..e4937892e2f7 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -122,7 +122,7 @@ interface IWifiManager DhcpInfo getDhcpInfo(); - void setScanAlwaysAvailable(boolean isAvailable); + void setScanAlwaysAvailable(boolean isAvailable, String packageName); boolean isScanAlwaysAvailable(); @@ -142,9 +142,9 @@ interface IWifiManager void updateInterfaceIpState(String ifaceName, int mode); - boolean startSoftAp(in WifiConfiguration wifiConfig); + boolean startSoftAp(in WifiConfiguration wifiConfig, String packageName); - boolean startTetheredHotspot(in SoftApConfiguration softApConfig); + boolean startTetheredHotspot(in SoftApConfiguration softApConfig, String packageName); boolean stopSoftAp(); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index ae834f929691..b28b902910bf 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -2802,7 +2802,7 @@ public class WifiManager { @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanAlwaysAvailable(boolean isAvailable) { try { - mService.setScanAlwaysAvailable(isAvailable); + mService.setScanAlwaysAvailable(isAvailable, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3035,7 +3035,7 @@ public class WifiManager { }) public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) { try { - return mService.startSoftAp(wifiConfig); + return mService.startSoftAp(wifiConfig, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3059,7 +3059,7 @@ public class WifiManager { }) public boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig) { try { - return mService.startTetheredHotspot(softApConfig); + return mService.startTetheredHotspot(softApConfig, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index cba1690ac635..e7f1916c9e82 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -218,10 +218,10 @@ public class WifiManagerTest { */ @Test public void testStartSoftApCallsServiceWithWifiConfig() throws Exception { - when(mWifiService.startSoftAp(eq(mApConfig))).thenReturn(true); + when(mWifiService.startSoftAp(mApConfig, TEST_PACKAGE_NAME)).thenReturn(true); assertTrue(mWifiManager.startSoftAp(mApConfig)); - when(mWifiService.startSoftAp(eq(mApConfig))).thenReturn(false); + when(mWifiService.startSoftAp(mApConfig, TEST_PACKAGE_NAME)).thenReturn(false); assertFalse(mWifiManager.startSoftAp(mApConfig)); } @@ -231,10 +231,10 @@ public class WifiManagerTest { */ @Test public void testStartSoftApCallsServiceWithNullConfig() throws Exception { - when(mWifiService.startSoftAp(eq(null))).thenReturn(true); + when(mWifiService.startSoftAp(null, TEST_PACKAGE_NAME)).thenReturn(true); assertTrue(mWifiManager.startSoftAp(null)); - when(mWifiService.startSoftAp(eq(null))).thenReturn(false); + when(mWifiService.startSoftAp(null, TEST_PACKAGE_NAME)).thenReturn(false); assertFalse(mWifiManager.startSoftAp(null)); } @@ -257,10 +257,12 @@ public class WifiManagerTest { @Test public void testStartTetheredHotspotCallsServiceWithSoftApConfig() throws Exception { SoftApConfiguration softApConfig = generatorTestSoftApConfig(); - when(mWifiService.startTetheredHotspot(eq(softApConfig))).thenReturn(true); + when(mWifiService.startTetheredHotspot(softApConfig, TEST_PACKAGE_NAME)) + .thenReturn(true); assertTrue(mWifiManager.startTetheredHotspot(softApConfig)); - when(mWifiService.startTetheredHotspot(eq(softApConfig))).thenReturn(false); + when(mWifiService.startTetheredHotspot(softApConfig, TEST_PACKAGE_NAME)) + .thenReturn(false); assertFalse(mWifiManager.startTetheredHotspot(softApConfig)); } @@ -270,10 +272,10 @@ public class WifiManagerTest { */ @Test public void testStartTetheredHotspotCallsServiceWithNullConfig() throws Exception { - when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(true); + when(mWifiService.startTetheredHotspot(null, TEST_PACKAGE_NAME)).thenReturn(true); assertTrue(mWifiManager.startTetheredHotspot(null)); - when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(false); + when(mWifiService.startTetheredHotspot(null, TEST_PACKAGE_NAME)).thenReturn(false); assertFalse(mWifiManager.startTetheredHotspot(null)); } @@ -2375,7 +2377,7 @@ public class WifiManagerTest { @Test public void testScanAvailable() throws Exception { mWifiManager.setScanAlwaysAvailable(true); - verify(mWifiService).setScanAlwaysAvailable(true); + verify(mWifiService).setScanAlwaysAvailable(true, TEST_PACKAGE_NAME); when(mWifiService.isScanAlwaysAvailable()).thenReturn(false); assertFalse(mWifiManager.isScanAlwaysAvailable()); |