Merge "Implement a PowerCalculator to attribute system service power to apps"
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 029699e..ca921ff 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -150,12 +150,10 @@
":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 9ac8c87..4f6c21a 100644
--- a/apct-tests/perftests/autofill/Android.bp
+++ b/apct-tests/perftests/autofill/Android.bp
@@ -20,8 +20,9 @@
"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 51f6a76..57595a2 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 29f9f94..eee7bdc 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/api/current.txt b/api/current.txt
index 09de005..b70103b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11900,6 +11900,7 @@
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/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 36ff20f..9c79612 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -417,7 +417,7 @@
// 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 @@
}
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 a46a54c..dec4a56 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -51,12 +51,11 @@
"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 @@
png = true;
break;
case 'd':
- displayId = atoll(optarg);
+ displayId = PhysicalDisplayId(atoll(optarg));
break;
case '?':
case 'h':
@@ -183,7 +182,7 @@
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 95b179a..14b74da 100644
--- a/cmds/uiautomator/library/Android.bp
+++ b/cmds/uiautomator/library/Android.bp
@@ -54,7 +54,6 @@
],
installable: false,
custom_template: "droiddoc-templates-sdk",
- create_stubs: false,
}
java_library_static {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2be9282..522b8cc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2880,7 +2880,7 @@
* 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 9cf0d2a..1f8cf8a 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -341,7 +341,8 @@
/** @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/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index bd02210..31c77ee 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -963,7 +963,7 @@
/** @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 @@
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 df9db27..bed7b26 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2075,7 +2075,8 @@
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 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/view/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java
index 3d05e2a..108345e 100644
--- a/core/java/android/view/InputApplicationHandle.java
+++ b/core/java/android/view/InputApplicationHandle.java
@@ -34,7 +34,7 @@
public String name;
// Dispatching timeout.
- public long dispatchingTimeoutNanos;
+ public long dispatchingTimeoutMillis;
public final IBinder token;
@@ -46,7 +46,7 @@
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 a7e0305..e341845 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -50,7 +50,7 @@
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 0818abe..8917821 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -23358,7 +23358,7 @@
* 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/widget/inline/InlineContentView.java b/core/java/android/widget/inline/InlineContentView.java
index 699a7735..9712311 100644
--- a/core/java/android/widget/inline/InlineContentView.java
+++ b/core/java/android/widget/inline/InlineContentView.java
@@ -59,7 +59,7 @@
*/
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/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
index ba60fa5..b42ea7d 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 @@
*/
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 ff73c74..7756a62 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -30,7 +30,7 @@
static struct {
jfieldID ptr;
jfieldID name;
- jfieldID dispatchingTimeoutNanos;
+ jfieldID dispatchingTimeoutMillis;
jfieldID token;
} gInputApplicationHandleClassInfo;
@@ -61,8 +61,8 @@
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 @@
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 796c5c4..ecdba3f 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -47,7 +47,7 @@
jfieldID name;
jfieldID layoutParamsFlags;
jfieldID layoutParamsType;
- jfieldID dispatchingTimeoutNanos;
+ jfieldID dispatchingTimeoutMillis;
jfieldID frameLeft;
jfieldID frameTop;
jfieldID frameRight;
@@ -118,8 +118,8 @@
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 @@
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 7daefd3..e715be2 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -95,8 +95,8 @@
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 @@
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 @@
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 814a07e..d6a773f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -703,7 +703,7 @@
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 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/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 0f68376..e76aace 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -228,9 +228,12 @@
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 542737b4..ef68814 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -21,6 +21,8 @@
import android.annotation.Nullable;
import android.location.util.identity.CallerIdentity;
+import java.util.List;
+
/**
* Location manager local system service interface.
*
@@ -29,6 +31,16 @@
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 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 @@
*/
// 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 bb36c2a..280bd05 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -32,6 +32,8 @@
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 @@
@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 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 LocationRequest(LocationRequest src) {
this(
src.mProvider,
- src.mCoarseLocation,
src.mQuality,
src.mInterval,
src.mFastestInterval,
@@ -293,7 +291,6 @@
private LocationRequest(
@NonNull String provider,
- boolean coarseLocation,
int quality,
long intervalMs,
long fastestIntervalMs,
@@ -310,7 +307,6 @@
checkQuality(quality);
mProvider = provider;
- mCoarseLocation = coarseLocation;
mQuality = quality;
mInterval = intervalMs;
mFastestInterval = fastestIntervalMs;
@@ -327,20 +323,6 @@
}
/**
- * @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 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 @@
@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 @@
}
}
+ @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 @@
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/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 54618a5..70ef69d 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11900,6 +11900,7 @@
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/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java
index 5f9665f..0452b83 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 @@
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 @@
// 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 d769cac..86b86d4 100644
--- a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
@@ -63,7 +63,7 @@
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 @@
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 @@
}
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 31f1170..f77294e 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 @@
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 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 @@
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 @@
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mTestableLooper.processAllMessages();
+ waitForIdleSync();
- verify(mTestHandler).post(any());
+ 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 @@
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 @@
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 @@
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mTestableLooper.processAllMessages();
+ waitForIdleSync();
- verify(mTestHandler, never()).post(any());
+ mHandler.post(() -> {
+ verify(mTestHandler, never()).post(any());
+ });
}
}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 704d264..6751fa4 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 585924d..470e780 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 39777cd..9acfa0d 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 e0455cb..f993dba 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 6565d53..6a50661 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 3ced29b..cf988ab 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 01d7682..8f71509 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 d042c0f..747cb26 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 2db23f7..87f3b7a 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 f26fe9d..e329c99 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 3ca9d52..5ffdacd 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 a5532e0..0aef99f 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 8ca22d7..98068cb 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 6b0ae2e2..0837ad3 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 4d7c882..1f9d977 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 cc3b2aa..abe61a9 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 a9f039a..959ad46 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 cc3b2aa..abe61a9 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 cc3b2aa..abe61a9 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 41c20e0..738dd2a 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 287a1ac..d1e4fb5 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 9d34557..9ad71e2 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 d003ef0..14d3b57 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 0042321..dcdadbd 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 1c08815..f3b22d3 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 3945e55..3d28f1d 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 140d4ce..87d3de1 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 1b1ae8e..ddf2bc0 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 f9d57c4..af43099 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 aa1f960..3261f69 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 9b6a27a..904a70e 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 14e3330..3edc452 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 d16ff03..fec2dd6 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 b010b50..f219d24 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 37cf189f..3ab50cc 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 42ccd53..a6f8846 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 0ebc341..caf2323 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 50fdfc9..8d18727 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 fb7d00f..fff881c 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 3537cea..5e579b7 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 9b671a8..1b5fae9 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 279aca0..9c290e9 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 03065e8..2878db1 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 71c5e49..7b01056 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 5d82eae..696ed29 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 2702392..c4b5f7e 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 7a2c338..a72861e 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 b73aa66..c72bf21 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 8e5d24c..d95e57d 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 34299d8..fb7b634 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 c95f8bf..3c281c8 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 8407db6..37fc5b4 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 c50f365..360f158 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 a0a434f..68356df2 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 fa49929..3729a83 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 aeaba31..0e0e761 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 4a2c171..762d830 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 32cc39e..83b72e9 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 8e5bf25..d200f50 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 1570013..354ee12 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 1120c50..095412c 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 4214a27..895a987 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 508cbfc..2d9f037 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 4214a27..895a987 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 663d3f7..728db17 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 9c0a2f5..ff2115e 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 8d0e93e..a883cc6 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 f39a741..05c6379 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 233c8e4..fd216e8 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 6af1062..002c7fc 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 74c2aec..25a1beb7 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 fe1b0a8..352cb0a 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 5c80627..d2891a0 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 241644f..7837dd8 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 a9ec2ea9..60001a0 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 b8343c6..defc33e 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 8aeb392..5d4e975 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 e6d9380..f01f3fa 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 cf1fafd..9ca2f06 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 b7fbe6f..8953f50 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 f81731a..f25b3ac 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 b7ccf8d..b5798f3 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 75c1333..c4dcfff 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 26ddfb1..e04651c 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 72ea043..a1ae6b6 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 6b8739f..2dafad8 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/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index c1543fd..bfd5b1cc 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 @@
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/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index 3c641af..ed870f8 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 e72c0e8..0000000
--- 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/strings.xml b/packages/SystemUI/res/values/strings.xml
index 77d3f45..823c1ff 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/controls/CustomIconCache.kt b/packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt
new file mode 100644
index 0000000..cca0f16
--- /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 ff40a8a..f68388d 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.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 @@
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 @@
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 4ef64a5..ad0e7a5 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 @@
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 @@
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 5242501..f9ce636 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 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 @@
* @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 @@
}
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 22d6b6b..e15380b 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 @@
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 1eb7e21..5f75c96 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.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 @@
@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 @@
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 de53168..7f610d1 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 @@
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 @@
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 @@
}
}
+ 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 3096908..77cac50 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 @@
private val mainExecutor: DelayableExecutor,
private val dismissCallback: () -> Unit,
private var translationChangedListener: () -> Unit,
+ private val closeGuts: () -> Unit,
private val falsingManager: FalsingManager
) {
/**
@@ -452,6 +453,7 @@
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 3fc162e..e55678dc 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.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 javax.inject.Inject;
+import dagger.Lazy;
+
/**
* A view controller used for Media Playback.
*/
@@ -59,6 +64,8 @@
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 @@
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 @@
@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 @@
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 @@
PendingIntent clickIntent = data.getClickIntent();
if (clickIntent != null) {
mViewHolder.getPlayer().setOnClickListener(v -> {
+ if (mMediaViewController.isGutsVisible()) return;
mActivityStarter.postStartActivityDismissingKeyguard(clickIntent);
});
}
@@ -329,14 +357,38 @@
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 d82150f2..8a51c85 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.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 @@
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 @@
constructor(
context: Context,
@Background backgroundExecutor: Executor,
- @Main foregroundExecutor: Executor,
+ @Main foregroundExecutor: DelayableExecutor,
mediaControllerFactory: MediaControllerFactory,
dumpManager: DumpManager,
broadcastDispatcher: BroadcastDispatcher,
@@ -183,10 +184,7 @@
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 @@
}
}
+ 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 fc33391..70f01d5 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 @@
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 38817d7..92eeed4 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 @@
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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 600fdc2..11551ac 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.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 @@
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 @@
it.registerLightSource(action2)
it.registerLightSource(action3)
it.registerLightSource(action4)
+ it.registerLightSource(cancel)
+ it.registerLightSource(dismiss)
+ it.registerLightSource(settings)
}
}
@@ -83,9 +92,6 @@
}
}
- // Settings screen
- val options = itemView.requireViewById<View>(R.id.qs_media_controls_options)
-
companion object {
/**
* Creates a PlayerViewHolder.
@@ -105,5 +111,29 @@
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/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 7c3743b..025341c 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 @@
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 @@
@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 @@
.getInteger(R.integer.config_pipResizeAnimationDuration);
mSurfaceTransactionHelper = surfaceTransactionHelper;
mPipAnimationController = pipAnimationController;
+ mPipUiEventLoggerLogger = pipUiEventLogger;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mSplitDivider = divider;
displayController.addDisplayWindowListener(this);
@@ -279,6 +282,8 @@
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 @@
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 @@
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 @@
mPictureInPictureParams = null;
mInPip = false;
mExitingPip = false;
+ mPipUiEventLoggerLogger.setTaskInfo(null);
}
@Override
@@ -628,7 +638,7 @@
* {@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 @@
/**
* 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 0000000..5e2cd9c
--- /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 6e75253..9dfa864 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.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 @@
PipSnapAlgorithm pipSnapAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
SysUiState sysUiState,
- ConfigurationController configController) {
+ ConfigurationController configController,
+ PipUiEventLogger pipUiEventLogger) {
mContext = context;
mActivityManager = ActivityManager.getService();
@@ -280,7 +282,8 @@
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 @@
}
@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 @@
/**
* 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 ee8f295..19138fdb 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 @@
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 d884fa9..9c42f8b 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.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 @@
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 @@
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 @@
mMovementBoundsSupplier = movementBoundsSupplier;
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
mSysUiState = sysUiState;
+ mPipUiEventLogger = pipUiEventLogger;
context.getDisplay().getRealSize(mMaxSize);
reloadResources();
@@ -181,6 +186,7 @@
void onActivityUnpinned() {
mIsAttached = false;
+ mUserResizeBounds.setEmpty();
updateIsEnabled();
}
@@ -329,6 +335,7 @@
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 @@
resetState();
});
});
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
} else {
resetState();
}
@@ -351,6 +360,14 @@
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 7fc2823..b20ea4e 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.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.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 @@
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 @@
// 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 @@
@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 @@
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 @@
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 @@
pipTaskOrganizer, pipSnapAlgorithm, this::onAccessibilityShowMenu,
this::updateMovementBounds, mHandler);
+ mPipUiEventLogger = pipUiEventLogger;
+
mTargetView = new DismissCircleView(context);
mTargetViewContainer = new FrameLayout(context);
mTargetViewContainer.setBackgroundDrawable(
@@ -307,11 +304,8 @@
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 @@
mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
}
- mResizedBounds.setEmpty();
mPipResizeGestureHandler.onActivityUnpinned();
}
@@ -393,9 +386,8 @@
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 @@
// 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 @@
}
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 @@
// 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 @@
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 2138f09..0167028 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 @@
};
@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/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 560998b..22c735d 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 @@
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/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 8c485a6..d2aaaede 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 @@
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/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 99cb476..a87311a 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 @@
super.onClosingFinished();
resetHorizontalPanelPosition();
setClosingWithAlphaFadeout(false);
+ mMediaHierarchyManager.closeGuts();
}
private void setClosingWithAlphaFadeout(boolean closing) {
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 0000000..4d0f2ed
--- /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 ce33a8d..f0003ed 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.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 @@
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 @@
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 @@
@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 @@
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 ac8c671..4c54954 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 @@
@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 c63781c..8a30b00 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.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 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 @@
@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 @@
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 @@
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 @@
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 @@
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 59c2d0e..3789e6e 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 @@
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 91c5ff8..d86dfa5 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 @@
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 96bb521..9f67722 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.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 @@
@Mock
private SysUiState mSysUiState;
+ @Mock
+ private PipUiEventLogger mPipUiEventLogger;
+
private PipSnapAlgorithm mPipSnapAlgorithm;
private PipMotionHelper mMotionHelper;
private PipResizeGestureHandler mPipResizeGestureHandler;
@@ -104,7 +108,7 @@
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/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 103e558..cccb65d 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.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 2d276bb..6b54791 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.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 ae87eef..781f875 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.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 a16fb5e..86dacc1 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.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 0e1c560..b2f57d0 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 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 486939d..4f509ea 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.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 9b9dcde..5f8d299 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 6a33d55..0ee7a99 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 aa58a4b..fd9e360 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 @@
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 @@
// 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 @@
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 @@
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 cfc6575..7dd5290 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 @@
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 e1771a5..5783805 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 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 @@
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 @@
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 @@
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 @@
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 2c0df6f..8e93c2e 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 @@
@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 @@
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 a9ac4e2..dc0940c 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 @@
.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 @@
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 b0e401b..3f712dd 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -3706,33 +3706,40 @@
// 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 2cd4c69..b134022 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
* (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 1093515..e76ec74 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -79,6 +79,7 @@
":framework_native_aidl",
":gsiservice_aidl",
":idmap2_aidl",
+ ":inputconstants_aidl",
":installd_aidl",
":storaged_aidl",
":vold_aidl",
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 72f29b4..2534a53 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.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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
private void doVibratorSetAmplitude(int amplitude) {
- if (mSupportsAmplitudeControl) {
+ if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
mNativeWrapper.vibratorSetAmplitude(amplitude);
}
}
@@ -1708,14 +1716,25 @@
@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 @@
/** 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 @@
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 @@
@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 @@
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 c187772..e546a28 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_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_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;
@@ -18353,19 +18354,20 @@
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/PendingTempWhitelists.java b/services/core/java/com/android/server/am/PendingTempWhitelists.java
index b36e3c7..50d58f0 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 @@
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 cd4302b..1647fda 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1522,8 +1522,8 @@
}
}
- public long getInputDispatchingTimeout() {
- return mWindowProcessController.getInputDispatchingTimeout();
+ public long getInputDispatchingTimeoutMillis() {
+ return mWindowProcessController.getInputDispatchingTimeoutMillis();
}
public int getProcessClassEnum() {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e6480fc..ee441bf 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 @@
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 45f95fd..2bbbbf1 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 @@
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 @@
/*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 @@
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/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 5e8f1ef..ded0f9a3 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 @@
// 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 @@
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/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 74ed815..5a423793 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.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 @@
// 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 @@
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 @@
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 @@
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
+ synchronized (mGestureMonitorPidsLock) {
+ mGestureMonitorPidsByToken.remove(inputChannel.getToken());
+ }
nativeUnregisterInputChannel(mPtr, inputChannel);
}
@@ -1838,6 +1850,7 @@
if (dumpStr != null) {
pw.println(dumpStr);
dumpAssociations(pw);
+ dumpGestureMonitorPidsByToken(pw);
}
}
@@ -1861,6 +1874,19 @@
}
}
+ 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 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 @@
// Native callback.
private void notifyInputChannelBroken(IBinder token) {
+ synchronized (mGestureMonitorPidsLock) {
+ mGestureMonitorPidsByToken.remove(token);
+ }
mWindowManagerCallbacks.notifyInputChannelBroken(token);
}
@@ -1959,8 +1989,12 @@
// 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 @@
* 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 @@
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 1f458ed..6f35c8b 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 Location createCoarse(Location fine) {
synchronized (this) {
if (fine == mCachedFineLocation) {
- return new Location(mCachedCoarseLocation);
+ return mCachedCoarseLocation;
}
}
@@ -154,7 +154,7 @@
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 d3558f1..71f1833 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.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.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.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 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 Context mContext;
+ private final Injector mInjector;
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 GeofenceManager mGeofenceManager;
-
@Nullable private volatile GnssManagerService mGnssManagerService = null;
-
- private final PassiveLocationProviderManager mPassiveManager;
-
- private PowerManager mPowerManager;
-
private GeocoderProxy mGeocodeProvider;
@GuardedBy("mLock")
@@ -296,46 +224,30 @@
@GuardedBy("mLock")
private boolean mExtraLocationControllerPackageEnabled;
- // @GuardedBy("mLock")
- // hold lock for write or to prevent write, no lock for read
- final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
+ // location provider managers
+
+ private final PassiveLocationProviderManager mPassiveManager;
+
+ // @GuardedBy("mProviderManagers")
+ // hold lock for writes, no lock necessary for simple reads
+ private 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<>();
-
- private final LocationRequestStatistics mRequestStatistics = new LocationRequestStatistics();
-
- @GuardedBy("mLock")
- @PowerManager.LocationPowerSaveMode
- private int mBatterySaverMode;
-
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 @@
permissionManagerInternal.setLocationExtraPackagesProvider(
userId -> mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationExtraPackageNames));
+ }
- // most startup is deferred until systemReady()
+ @Nullable
+ private LocationProviderManager getLocationProviderManager(String providerName) {
+ if (providerName == null) {
+ return null;
+ }
+
+ for (LocationProviderManager manager : mProviderManagers) {
+ if (providerName.equals(manager.getName())) {
+ return manager;
+ }
+ }
+
+ return null;
+ }
+
+ private LocationProviderManager getOrAddLocationProviderManager(String providerName) {
+ synchronized (mProviderManagers) {
+ for (LocationProviderManager manager : mProviderManagers) {
+ if (providerName.equals(manager.getName())) {
+ return manager;
+ }
+ }
+
+ LocationProviderManager manager = new LocationProviderManager(mContext, mInjector,
+ providerName, mPassiveManager);
+ addLocationProviderManager(manager, null);
+ return manager;
+ }
+ }
+
+ private void addLocationProviderManager(LocationProviderManager manager,
+ @Nullable AbstractLocationProvider realProvider) {
+ synchronized (mProviderManagers) {
+ Preconditions.checkState(getLocationProviderManager(manager.getName()) == null);
+
+ manager.startManager();
+ if (realProvider != null) {
+ manager.setRealProvider(realProvider);
+ }
+ mProviderManagers.add(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();
+ }
}
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);
- }
+ mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
+ this::onLocationModeChanged);
}
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));
- }
-
- 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);
- }
- }
- }
- }
-
- 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);
- }
- }
- }
-
- 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);
- }
- }
-
- // perform removal outside of mReceivers loop
- if (deadReceivers != null) {
- for (Receiver receiver : deadReceivers) {
- removeUpdatesLocked(receiver);
- }
- }
- }
- }
-
- 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 onBackgroundThrottleIntervalChanged() {
- synchronized (mLock) {
- for (LocationProviderManager manager : mProviderManagers) {
- applyRequirementsLocked(manager);
- }
- }
- }
-
- private void onBackgroundThrottleWhitelistChanged() {
- synchronized (mLock) {
- for (LocationProviderManager manager : mProviderManagers) {
- applyRequirementsLocked(manager);
- }
- }
- }
-
- private void onIgnoreSettingsWhitelistChanged() {
- synchronized (mLock) {
- for (LocationProviderManager manager : mProviderManagers) {
- applyRequirementsLocked(manager);
- }
- }
- }
-
- @GuardedBy("mLock")
- private void initializeProvidersLocked() {
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 @@
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 @@
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 @@
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();
+ private void onLocationModeChanged(int userId) {
+ boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
+ LocationManager.invalidateLocalLocationEnabledCaches();
- 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);
+ if (D) {
+ Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
}
- 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 decrementPendingBroadcastsLocked() {
- if (--mPendingBroadcasts == 0) {
- // so wakelock calls will succeed
- long identity = Binder.clearCallingIdentity();
- try {
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- 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 @@
}
}
- @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 @@
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 @@
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 @@
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);
- }
- 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);
- }
- }
- }
-
- @GuardedBy("mLock")
- private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver) {
- String provider = request.getProvider();
-
- LocationProviderManager manager = getLocationProviderManager(provider);
- if (manager == null) {
- throw new IllegalArgumentException("provider doesn't exist: " + provider);
+ permission.WRITE_SECURE_SETTINGS,
+ "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
}
- UpdateRecord record = new UpdateRecord(provider, request, receiver);
-
- UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, record);
- if (oldRecord != null) {
- oldRecord.disposeLocked(false);
+ LocationRequest sanitized = new LocationRequest(request);
+ if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE) != PERMISSION_GRANTED) {
+ sanitized.setLowPowerMode(false);
}
-
- 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 (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;
}
- 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);
+ 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);
+ }
}
+ if (sanitized.getFastestInterval() > sanitized.getInterval()) {
+ sanitized.setFastestInterval(request.getInterval());
+ }
+ 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);
+ }
+ }
+ }
+
+ 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())) {
+ // 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());
+ if (manager == null) {
return null;
}
- synchronized (mLock) {
- LocationProviderManager manager = getLocationProviderManager(request.getProvider());
- if (manager == null) {
- return null;
- }
- if (!manager.isEnabled(identity.getUserId()) && !request.isLocationSettingsIgnored()) {
- return null;
- }
+ Location location = manager.getLastLocation(request, identity, permissionLevel);
- // appops check should always be right before delivery
- if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
- identity)) {
- return null;
- }
-
- Location location = manager.getLastLocation(identity.getUserId(), 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);
- }
+ // clients in the system process must have an attribution tag set
+ Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
- GetCurrentLocationTransport transport = new GetCurrentLocationTransport(callback);
+ request = validateAndSanitizeLocationRequest(request, permissionLevel);
- if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
- identity.getPackageName())) {
- transport.deliverResult(null);
- return;
- }
- if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
- transport.deliverResult(null);
- return;
- }
+ LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+ Preconditions.checkArgument(manager != null,
+ "provider \"" + request.getProvider() + "\" does not exist");
- 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());
-
- 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;
- }
-
- 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;
- }
- }
- }
-
- 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 @@
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 @@
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 @@
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 @@
if (provider != null && !provider.equals(manager.getName())) {
continue;
}
- CallerIdentity identity = manager.getProviderIdentity();
+ CallerIdentity identity = manager.getIdentity();
if (identity == null) {
continue;
}
@@ -2406,7 +920,7 @@
return Collections.emptyList();
}
- CallerIdentity identity = manager.getProviderIdentity();
+ CallerIdentity identity = manager.getIdentity();
if (identity == null) {
return Collections.emptyList();
}
@@ -2454,164 +968,26 @@
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 @@
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 @@
}
}
- // 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 @@
manager.setMockProvider(null);
if (!manager.hasProvider()) {
- mProviderManagers.remove(manager);
+ removeLocationProviderManager(manager);
}
}
}
@@ -2702,7 +1066,7 @@
// 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 @@
// 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 @@
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();
+ 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();
+
+ mInjector.getLocationRequestStatistics().history.dump(ipw);
+
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();
-
- mRequestStatistics.history.dump(ipw);
-
- if (mExtraLocationControllerPackage != null) {
- ipw.println(
- "Location Controller Extra Package: " + mExtraLocationControllerPackage
- + (mExtraLocationControllerPackageEnabled ? " [enabled]"
- : "[disabled]"));
- }
+ if (mExtraLocationControllerPackage != null) {
+ ipw.println(
+ "Location Controller Extra Package: " + mExtraLocationControllerPackage
+ + (mExtraLocationControllerPackageEnabled ? " [enabled]"
+ : "[disabled]"));
}
}
@@ -2857,47 +1196,6 @@
}
}
- 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 @@
}
@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;
- }
- }
+ public void addProviderEnabledListener(String provider, ProviderEnabledListener listener) {
+ LocationProviderManager manager = Objects.requireNonNull(
+ getLocationProviderManager(provider));
+ manager.addEnabledListener(listener);
+ }
- return false;
+ @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) {
+ LocationProviderManager manager = getLocationProviderManager(provider);
+ if (manager == null) {
+ return false;
+ } else {
+ return identity.equals(manager.getIdentity());
+ }
}
@Override
@@ -2935,6 +1244,13 @@
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 b9d86c8..0000000
--- 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 0000000..d4f8c7e
--- /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 c5bd5e6..fc88f14 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 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 54af1c8..d8d435a 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 @@
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 0000000..d4999ab
--- /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 0b7968b..1b599b0 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 @@
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 @@
}
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 @@
}
mUserInfoHelper.addListener(mUserChangedListener);
- mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
+ mLocationManagerInternal.addProviderEnabledListener(GPS_PROVIDER,
+ mProviderEnabledChangedListener);
mSettingsHelper.addOnBackgroundThrottlePackageWhitelistChangedListener(
mBackgroundThrottlePackageWhitelistChangedListener);
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
@@ -279,7 +283,8 @@
}
mUserInfoHelper.removeListener(mUserChangedListener);
- mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
+ mLocationManagerInternal.removeProviderEnabledListener(GPS_PROVIDER,
+ mProviderEnabledChangedListener);
mSettingsHelper.removeOnBackgroundThrottlePackageWhitelistChangedListener(
mBackgroundThrottlePackageWhitelistChangedListener);
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
@@ -294,7 +299,8 @@
}
}
- 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/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index 528cf8a..f94de9b 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 @@
* 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 @@
mCurrentRequest = null;
if (mServiceRegistered) {
mServiceRegistered = false;
+ mCurrentRequest = null;
unregisterWithService();
}
return;
@@ -376,11 +377,15 @@
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 @@
}
/**
- * 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 0bdd131..ac56c51 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.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 @@
*/
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 @@
// 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 @@
/**
* 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 @@
*/
protected void onInactive() {}
- final boolean isActive() {
+ public final boolean isActive() {
return mActive;
}
@@ -120,7 +124,7 @@
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 6a815ea..0698cca 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 @@
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/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 249b6801..07527c2 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 @@
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 @@
}
@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 @@
}
@Override
- boolean abortStagedSession(int sessionId) throws PackageManagerException {
+ boolean abortStagedSession(int sessionId) {
throw new UnsupportedOperationException();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7765f18..ca8a68b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3321,7 +3321,8 @@
/** {@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 @@
}
/** {@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 @@
/** {@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 a726c8d..dbdcc4f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -23643,8 +23643,20 @@
}
}
- 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/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 89ed3c7..700f7be 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 @@
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 @@
}
};
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 89bdb3e..f9bf54a 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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
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 @@
// 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 @@
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 @@
+ 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 @@
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 @@
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 @@
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 @@
} 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 8f11fd5..e3bee72 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 @@
}
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 @@
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/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b72ff2d..1b4fac6 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_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.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 @@
*/
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 @@
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 @@
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 @@
} 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 ed1ea35..a05289f 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.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;
@@ -788,7 +788,7 @@
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 */);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 777ddda..2dc22ec 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 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 5534b8c..627361d 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.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.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.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 @@
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 @@
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 @@
@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 @@
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 @@
// 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 @@
"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 @@
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 @@
r.pictureInPictureArgs.getAspectRatio());
stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
}
- logPictureInPictureArgs(params);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -4157,18 +4147,6 @@
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 @@
}
}
- 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 @@
}
/**
- * @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 @@
}
@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 f840d92..22dd1d3 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 @@
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 @@
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 852b3672..3b24584b 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 @@
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 9c4ac89..e166bfc 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_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 @@
*/
@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 @@
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 20f1b9f..791f471 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 @@
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 @@
} 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 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 28dcbcd..dccd3a6 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 @@
| 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 c7a438d..8ef57f7 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.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 @@
}
/**
- * @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 @@
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 @@
return false;
}
- return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
+ return !(isTaskAllowlisted(task) || mLockTaskModeTasks.isEmpty());
}
private boolean isRecentsAllowed(int userId) {
@@ -356,7 +356,7 @@
return false;
default:
}
- return isPackageWhitelisted(userId, packageName);
+ return isPackageAllowlisted(userId, packageName);
}
private boolean isEmergencyCallTask(Task task) {
@@ -556,7 +556,7 @@
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 @@
/**
* 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 @@
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 @@
}
}
- 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 0f92bc8..61b6e0b 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 @@
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 @@
// 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 ba2c0b6..df53563 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 @@
}
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 ac96d144..1cb483c 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 @@
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 b71ecbb..ede6708 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 @@
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 f362005..be0815b0 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_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 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 @@
/** 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 @@
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 @@
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 @@
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 @@
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 @@
private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
if (mWmService.mDisableTransitionAnimation
|| !isVisible()
- || getDisplayContent().mAppTransition.isTransitionSet()
|| getSurfaceControl() == null
|| !isLeafTask()) {
return false;
@@ -7372,8 +7370,6 @@
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 c68b660..3fbc037 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 @@
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 @@
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 @@
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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ede92f0..0b1d6bc 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.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 @@
// 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 @@
| 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 df59c56..a58c564 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 @@
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.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.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 @@
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 ef78420..ab78e74 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.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 @@
}
}
- 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 0e1b2f2..4b5f38c 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -99,6 +99,7 @@
"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 05aa359..74e2328 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 @@
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 @@
} 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 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 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 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 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 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 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 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*, 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) {
- 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 {
+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;
}
+ 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 @@
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 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 7843663..0202c88 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 @@
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/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
index 44eb828..a398961 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/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 0a61c44..7a3a950 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 @@
}
@After
- public void resetStaticMocks() {
+ public void tearDown() {
+ mAppOpsService.shutdown();
+
mMockingSession.finishMocking();
}
@@ -216,9 +218,8 @@
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 @@
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 0000000..1cb004a
--- /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 c692253..1b6ac3c 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 @@
@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 @@
@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 @@
@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 @@
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/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index a137cde..044f819 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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
.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 @@
.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 @@
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 @@
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 @@
@Test
public void testUpdateLockTaskPackages() {
- String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
- String[] whitelist2 = {TEST_PACKAGE_NAME};
+ String[] allowlist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
+ String[] allowlist2 = {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));
+ // 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 @@
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 @@
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 @@
@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 @@
@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 @@
@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 @@
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 @@
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 @@
}
/**
- * @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/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index 51bae3a..08144c8 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 @@
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 0000000..9d35cbc
--- /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 0000000..4195df7
--- /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 0000000..c62db1ea
--- /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 0000000..4da3eca
--- /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 0000000..d83a457
--- /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 da6018e..530d0e4 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -29,6 +29,7 @@
"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 7cfbdc2..5285b04 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.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 @@
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 @@
@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 @@
@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 @@
@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);