Merge changes from topic "combine-libhidlbase-libhwbinder" into qt-dev
* changes:
libhwbinder users use libhidlbase
libmedia2_jni: remove libhwbinder_noltopgo ref
diff --git a/api/current.txt b/api/current.txt
index 9c90ee6..cd78602 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9808,6 +9808,7 @@
field public static final int BIND_IMPORTANT = 64; // 0x40
field public static final int BIND_INCLUDE_CAPABILITIES = 4096; // 0x1000
field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
+ field public static final int BIND_NOT_PERCEPTIBLE = 256; // 0x100
field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
field public static final String BIOMETRIC_SERVICE = "biometric";
field public static final String BLUETOOTH_SERVICE = "bluetooth";
diff --git a/api/test-current.txt b/api/test-current.txt
index be92106..74725bc 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3473,6 +3473,11 @@
method public boolean isInputMethodPickerShown();
}
+ public class InputMethodSystemProperty {
+ ctor public InputMethodSystemProperty();
+ field public static final boolean MULTI_CLIENT_IME_ENABLED;
+ }
+
}
package android.view.inspector {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index e920843..d457ff9 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -303,8 +303,12 @@
ContentCaptureSessionEvents content_capture_session_events = 208;
ContentCaptureFlushed content_capture_flushed = 209;
LocationManagerApiUsageReported location_manager_api_usage_reported = 210;
- ReviewPermissionsFragmentResultReported review_permissions_fragment_result_reported
- = 211 [(log_from_module) = "permissioncontroller"];
+ ReviewPermissionsFragmentResultReported review_permissions_fragment_result_reported =
+ 211 [(log_from_module) = "permissioncontroller"];
+ RuntimePermissionsUpgradeResult runtime_permissions_upgrade_result =
+ 212 [(log_from_module) = "permissioncontroller"];
+ GrantPermissionsActivityButtonActions grant_permissions_activity_button_actions =
+ 213 [(log_from_module) = "permissioncontroller"];
}
// Pulled events will start at field 10000.
@@ -6567,3 +6571,39 @@
// The result of the permission grant
optional bool permission_granted = 5;
}
+
+/**
+* Information about results of permission upgrade by RuntimePermissionsUpgradeController
+* Logged from: RuntimePermissionUpdgradeController
+*/
+message RuntimePermissionsUpgradeResult {
+ // Permission granted as result of upgrade
+ optional string permission_name = 1;
+
+ // UID of package granted permission
+ optional int32 uid = 2 [(is_uid) = true];
+
+ // Name of package granted permission
+ optional string package_name = 3;
+}
+
+/**
+* Information about a buttons presented in GrantPermissionsActivty and choice made by user
+*/
+message GrantPermissionsActivityButtonActions {
+ // Permission granted as result of upgrade
+ optional string permission_group_name = 1;
+
+ // UID of package granted permission
+ optional int32 uid = 2 [(is_uid) = true];
+
+ // Name of package requesting permission
+ optional string package_name = 3;
+
+ // Buttons presented in the dialog - bit flags, bit numbers are in accordance with
+ // LABEL_ constants in GrantPermissionActivity.java
+ optional int32 buttons_presented = 4;
+
+ // Button clicked by user - same as bit flags in buttons_presented with only single bit set
+ optional int32 button_clicked = 5;
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3a74f7d..546f000 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2080,7 +2080,7 @@
@Override
public final boolean queueIdle() {
doGcIfNeeded();
- nPurgePendingResources();
+ purgePendingResources();
return false;
}
}
@@ -2088,9 +2088,7 @@
final class PurgeIdler implements MessageQueue.IdleHandler {
@Override
public boolean queueIdle() {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "purgePendingResources");
- nPurgePendingResources();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ purgePendingResources();
return false;
}
}
@@ -2460,13 +2458,17 @@
}
void doGcIfNeeded() {
+ doGcIfNeeded("bg");
+ }
+
+ void doGcIfNeeded(String reason) {
mGcIdlerScheduled = false;
final long now = SystemClock.uptimeMillis();
//Slog.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime()
// + "m now=" + now);
if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
//Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!");
- BinderInternal.forceGc("bg");
+ BinderInternal.forceGc(reason);
}
}
@@ -6006,6 +6008,16 @@
WindowManagerGlobal.getInstance().trimMemory(level);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ if (SystemProperties.getInt("debug.am.run_gc_trim_level", Integer.MAX_VALUE) <= level) {
+ unscheduleGcIdler();
+ doGcIfNeeded("tm");
+ }
+ if (SystemProperties.getInt("debug.am.run_mallopt_trim_level", Integer.MAX_VALUE)
+ <= level) {
+ unschedulePurgeIdler();
+ purgePendingResources();
+ }
}
private void setupGraphicsSupport(Context context) {
@@ -7346,6 +7358,12 @@
throw new RuntimeException("Main thread loop unexpectedly exited");
}
+ private void purgePendingResources() {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "purgePendingResources");
+ nPurgePendingResources();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
// ------------------ Regular JNI ------------------------
private native void nPurgePendingResources();
private native void nDumpGraphicsInfo(FileDescriptor fd);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 529677a..c3dd827 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -234,7 +234,9 @@
BIND_ALLOW_OOM_MANAGEMENT,
BIND_WAIVE_PRIORITY,
BIND_IMPORTANT,
- BIND_ADJUST_WITH_ACTIVITY
+ BIND_ADJUST_WITH_ACTIVITY,
+ BIND_NOT_PERCEPTIBLE,
+ BIND_INCLUDE_CAPABILITIES
})
@Retention(RetentionPolicy.SOURCE)
public @interface BindServiceFlags {}
@@ -329,26 +331,25 @@
public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080;
/**
+ * Flag for {@link #bindService}: If binding from an app that is visible or user-perceptible,
+ * lower the target service's importance to below the perceptible level. This allows
+ * the system to (temporarily) expunge the bound process from memory to make room for more
+ * important user-perceptible processes.
+ */
+ public static final int BIND_NOT_PERCEPTIBLE = 0x00000100;
+
+ /**
* Flag for {@link #bindService}: If binding from an app that has specific capabilities
* due to its foreground state such as an activity or foreground service, then this flag will
* allow the bound app to get the same capabilities, as long as it has the required permissions
* as well.
*/
- public static final int BIND_INCLUDE_CAPABILITIES = 0x00001000;
+ public static final int BIND_INCLUDE_CAPABILITIES = 0x000001000;
/*********** Public flags above this line ***********/
/*********** Hidden flags below this line ***********/
/**
- * Flag for {@link #bindService}: If binding from something better than perceptible,
- * still set the adjust below perceptible. This would be used for bound services that can
- * afford to be evicted when under extreme memory pressure, but should be restarted as soon
- * as possible.
- * @hide
- */
- public static final int BIND_ADJUST_BELOW_PERCEPTIBLE = 0x00040000;
-
- /**
* Flag for {@link #bindService}: This flag is intended to be used only by the system to adjust
* the scheduling policy for IMEs (and any other out-of-process user-visible components that
* work closely with the top app) so that UI hosted in such services can have the same
@@ -473,7 +474,7 @@
*/
public static final int BIND_REDUCTION_FLAGS =
Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_WAIVE_PRIORITY
- | Context.BIND_ADJUST_BELOW_PERCEPTIBLE | Context.BIND_NOT_VISIBLE;
+ | Context.BIND_NOT_PERCEPTIBLE | Context.BIND_NOT_VISIBLE;
/** @hide */
@IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index fbfbfc0..111a8c4 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -655,7 +655,7 @@
* {@hide}
*/
@Deprecated
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
+ @UnsupportedAppUsage
public static final int TYPE_WIFI_P2P = 13;
/**
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index 46eddde..ec73866 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -44,9 +44,11 @@
* {@link SocketKeepalive.Callback#onStopped} if the operation was successful or
* {@link SocketKeepalive.Callback#onError} if an error occurred.
*
- * The device SHOULD support keepalive offload. If it does not, it MUST reply with
+ * For cellular, the device MUST support at least 1 keepalive slot.
+ *
+ * For WiFi, the device SHOULD support keepalive offload. If it does not, it MUST reply with
* {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload
- * request. If it does, it MUST support at least 3 concurrent keepalive slots per transport.
+ * request. If it does, it MUST support at least 3 concurrent keepalive slots.
*/
public abstract class SocketKeepalive implements AutoCloseable {
static final String TAG = "SocketKeepalive";
diff --git a/core/java/android/net/util/KeepaliveUtils.java b/core/java/android/net/util/KeepaliveUtils.java
index 569fed1..bfc4563 100644
--- a/core/java/android/net/util/KeepaliveUtils.java
+++ b/core/java/android/net/util/KeepaliveUtils.java
@@ -34,9 +34,6 @@
public static final String TAG = "KeepaliveUtils";
- // Minimum supported keepalive count per transport if the network supports keepalive.
- public static final int MIN_SUPPORTED_KEEPALIVE_COUNT = 3;
-
public static class KeepaliveDeviceConfigurationException extends AndroidRuntimeException {
public KeepaliveDeviceConfigurationException(final String msg) {
super(msg);
@@ -84,10 +81,7 @@
throw new KeepaliveDeviceConfigurationException("Invalid transport " + transport);
}
- // Customized values should be either 0 to indicate the network doesn't support
- // keepalive offload, or a positive value that is at least
- // MIN_SUPPORTED_KEEPALIVE_COUNT if supported.
- if (supported != 0 && supported < MIN_SUPPORTED_KEEPALIVE_COUNT) {
+ if (supported < 0) {
throw new KeepaliveDeviceConfigurationException(
"Invalid supported count " + supported + " for "
+ NetworkCapabilities.transportNameOf(transport));
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 45480cb..7d3b13b 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -77,6 +77,7 @@
private Provider mProvider;
private INotificationManager mNoMan;
+ boolean mIsConnected;
/**
* The {@link Intent} that must be declared as handled by the service.
@@ -179,7 +180,7 @@
try {
noMan.requestUnbindProvider(mProvider);
// Disable future messages.
- mProvider = null;
+ mIsConnected = false;
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -233,16 +234,16 @@
*/
@TestApi
public boolean isBound() {
- if (mProvider == null) {
+ if (!mIsConnected) {
Log.w(TAG, "Condition provider service not yet bound.");
- return false;
}
- return true;
+ return mIsConnected;
}
private final class Provider extends IConditionProvider.Stub {
@Override
public void onConnected() {
+ mIsConnected = true;
mHandler.obtainMessage(H.ON_CONNECTED).sendToTarget();
}
@@ -265,7 +266,7 @@
@Override
public void handleMessage(Message msg) {
String name = null;
- if (!isBound()) {
+ if (!mIsConnected) {
return;
}
try {
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 8512a0b..905c781 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -20,6 +20,7 @@
import android.annotation.UnsupportedAppUsage;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.Person;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -32,6 +33,8 @@
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import java.util.ArrayList;
+
/**
* Class encapsulating a Notification. Sent by the NotificationManagerService to clients including
* the status bar and any {@link android.service.notification.NotificationListenerService}s.
@@ -166,6 +169,7 @@
/**
* Returns true if application asked that this notification be part of a group.
+ *
* @hide
*/
public boolean isAppGroup() {
@@ -203,18 +207,16 @@
return 0;
}
- public static final @android.annotation.NonNull Parcelable.Creator<StatusBarNotification> CREATOR
- = new Parcelable.Creator<StatusBarNotification>()
- {
- public StatusBarNotification createFromParcel(Parcel parcel)
- {
- return new StatusBarNotification(parcel);
- }
+ public static final @android.annotation.NonNull
+ Parcelable.Creator<StatusBarNotification> CREATOR =
+ new Parcelable.Creator<StatusBarNotification>() {
+ public StatusBarNotification createFromParcel(Parcel parcel) {
+ return new StatusBarNotification(parcel);
+ }
- public StatusBarNotification[] newArray(int size)
- {
- return new StatusBarNotification[size];
- }
+ public StatusBarNotification[] newArray(int size) {
+ return new StatusBarNotification[size];
+ }
};
/**
@@ -243,14 +245,16 @@
this.key, this.notification);
}
- /** Convenience method to check the notification's flags for
+ /**
+ * Convenience method to check the notification's flags for
* {@link Notification#FLAG_ONGOING_EVENT}.
*/
public boolean isOngoing() {
return (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
}
- /** Convenience method to check the notification's flags for
+ /**
+ * Convenience method to check the notification's flags for
* either {@link Notification#FLAG_ONGOING_EVENT} or
* {@link Notification#FLAG_NO_CLEAR}.
*/
@@ -274,13 +278,15 @@
return pkg;
}
- /** The id supplied to {@link android.app.NotificationManager#notify(int,Notification)}. */
+ /** The id supplied to {@link android.app.NotificationManager#notify(int, Notification)}. */
public int getId() {
return id;
}
- /** The tag supplied to {@link android.app.NotificationManager#notify(int,Notification)},
- * or null if no tag was specified. */
+ /**
+ * The tag supplied to {@link android.app.NotificationManager#notify(int, Notification)},
+ * or null if no tag was specified.
+ */
public String getTag() {
return tag;
}
@@ -307,8 +313,10 @@
return initialPid;
}
- /** The {@link android.app.Notification} supplied to
- * {@link android.app.NotificationManager#notify(int,Notification)}. */
+ /**
+ * The {@link android.app.Notification} supplied to
+ * {@link android.app.NotificationManager#notify(int, Notification)}.
+ */
public Notification getNotification() {
return notification;
}
@@ -320,7 +328,8 @@
return user;
}
- /** The time (in {@link System#currentTimeMillis} time) the notification was posted,
+ /**
+ * The time (in {@link System#currentTimeMillis} time) the notification was posted,
* which may be different than {@link android.app.Notification#when}.
*/
public long getPostTime() {
@@ -343,6 +352,7 @@
/**
* The ID passed to setGroup(), or the override, or null.
+ *
* @hide
*/
public String getGroup() {
@@ -398,10 +408,11 @@
/**
* Returns a LogMaker that contains all basic information of the notification.
+ *
* @hide
*/
public LogMaker getLogMaker() {
- return new LogMaker(MetricsEvent.VIEW_UNKNOWN).setPackageName(getPackageName())
+ LogMaker logMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN).setPackageName(getPackageName())
.addTaggedData(MetricsEvent.NOTIFICATION_ID, getId())
.addTaggedData(MetricsEvent.NOTIFICATION_TAG, getTag())
.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID, getChannelIdLogTag())
@@ -410,6 +421,21 @@
getNotification().isGroupSummary() ? 1 : 0)
.addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CATEGORY,
getNotification().category);
+ if (getNotification().extras != null) {
+ // Log the style used, if present. We only log the hash here, as notification log
+ // events are frequent, while there are few styles (hence low chance of collisions).
+ String template = getNotification().extras.getString(Notification.EXTRA_TEMPLATE);
+ if (template != null && !template.isEmpty()) {
+ logMaker.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_STYLE,
+ template.hashCode());
+ }
+ ArrayList<Person> people = getNotification().extras.getParcelableArrayList(
+ Notification.EXTRA_PEOPLE_LIST);
+ if (people != null && !people.isEmpty()) {
+ logMaker.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_PEOPLE, people.size());
+ }
+ }
+ return logMaker;
}
private String getGroupLogTag() {
@@ -433,6 +459,6 @@
}
String hash = Integer.toHexString(logTag.hashCode());
return logTag.substring(0, MAX_LOG_TAG_LENGTH - hash.length() - 1) + "-"
- + hash;
+ + hash;
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodSystemProperty.java b/core/java/android/view/inputmethod/InputMethodSystemProperty.java
index 7c79d44..05143a1 100644
--- a/core/java/android/view/inputmethod/InputMethodSystemProperty.java
+++ b/core/java/android/view/inputmethod/InputMethodSystemProperty.java
@@ -17,6 +17,7 @@
package android.view.inputmethod;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.os.Build;
import android.os.SystemProperties;
@@ -26,6 +27,7 @@
*
* @hide
*/
+@TestApi
public class InputMethodSystemProperty {
/**
* System property key for the production use. The value must be either empty or a valid
@@ -87,6 +89,7 @@
*
* @hide
*/
+ @TestApi
public static final boolean MULTI_CLIENT_IME_ENABLED = (sMultiClientImeComponentName != null);
/**
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 1719015..ccafa64 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -277,7 +277,7 @@
mWindowElevation, mWindowCornerRadius,
mOverlay != null ? mOverlay : new ColorDrawable(Color.TRANSPARENT),
Handler.getMain() /* draw the magnifier on the UI thread */, mLock,
- mDestroyLock, mCallback);
+ mCallback);
}
}
performPixelCopy(startX, startY, true /* update window position */);
@@ -306,11 +306,9 @@
*/
public void dismiss() {
if (mWindow != null) {
- synchronized (mDestroyLock) {
- synchronized (mLock) {
- mWindow.destroy();
- mWindow = null;
- }
+ synchronized (mLock) {
+ mWindow.destroy();
+ mWindow = null;
}
mPrevShowSourceCoords.x = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
mPrevShowSourceCoords.y = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
@@ -835,24 +833,16 @@
private int mWindowPositionY;
private boolean mPendingWindowPositionUpdate;
- // The lock used to synchronize the UI and render threads when a #destroy
- // is performed on the UI thread and a frame callback on the render thread.
- // When both mLock and mDestroyLock need to be held at the same time,
- // mDestroyLock should be acquired before mLock in order to avoid deadlocks.
- private final Object mDestroyLock;
-
// The current content of the magnifier. It is mBitmap + mOverlay, only used for testing.
private Bitmap mCurrentContent;
InternalPopupWindow(final Context context, final Display display,
final SurfaceControl parentSurfaceControl, final int width, final int height,
final float elevation, final float cornerRadius, final Drawable overlay,
- final Handler handler, final Object lock, final Object destroyLock,
- final Callback callback) {
+ final Handler handler, final Object lock, final Callback callback) {
mDisplay = display;
mOverlay = overlay;
mLock = lock;
- mDestroyLock = destroyLock;
mCallback = callback;
mContentWidth = width;
@@ -1039,20 +1029,17 @@
}
/**
- * Destroys this instance.
+ * Destroys this instance. The method has to be called in a context holding {@link #mLock}.
*/
public void destroy() {
- synchronized (mDestroyLock) {
- mSurface.destroy();
- }
- synchronized (mLock) {
- mRenderer.destroy();
- mSurfaceControl.remove();
- mSurfaceSession.kill();
- mHandler.removeCallbacks(mMagnifierUpdater);
- if (mBitmap != null) {
- mBitmap.recycle();
- }
+ // Destroy the renderer. This will not proceed until pending frame callbacks complete.
+ mRenderer.destroy();
+ mSurface.destroy();
+ mSurfaceControl.remove();
+ mSurfaceSession.kill();
+ mHandler.removeCallbacks(mMagnifierUpdater);
+ if (mBitmap != null) {
+ mBitmap.recycle();
}
}
@@ -1090,24 +1077,20 @@
final int pendingY = mWindowPositionY;
callback = frame -> {
- synchronized (mDestroyLock) {
- if (!mSurface.isValid()) {
- return;
- }
- synchronized (mLock) {
- // Show or move the window at the content draw frame.
- SurfaceControl.openTransaction();
- mSurfaceControl.deferTransactionUntil(mSurface, frame);
- if (updateWindowPosition) {
- mSurfaceControl.setPosition(pendingX, pendingY);
- }
- if (firstDraw) {
- mSurfaceControl.setLayer(SURFACE_Z);
- mSurfaceControl.show();
- }
- SurfaceControl.closeTransaction();
- }
+ if (!mSurface.isValid()) {
+ return;
}
+ // Show or move the window at the content draw frame.
+ SurfaceControl.openTransaction();
+ mSurfaceControl.deferTransactionUntil(mSurface, frame);
+ if (updateWindowPosition) {
+ mSurfaceControl.setPosition(pendingX, pendingY);
+ }
+ if (firstDraw) {
+ mSurfaceControl.setLayer(SURFACE_Z);
+ mSurfaceControl.show();
+ }
+ SurfaceControl.closeTransaction();
};
mRenderer.setLightCenter(mDisplay, pendingX, pendingY);
} else {
diff --git a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
index 2ebf2fd..aacf63a 100644
--- a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
@@ -60,10 +60,16 @@
@Override // from AbstractRemoteService
protected void handleOnDestroy() {
- if (mPendingRequest != null) {
- mPendingRequest.cancel();
+ handleCancelPendingRequest();
+ }
+
+ protected BasePendingRequest<S, I> handleCancelPendingRequest() {
+ BasePendingRequest<S, I> pendingRequest = mPendingRequest;
+ if (pendingRequest != null) {
+ pendingRequest.cancel();
mPendingRequest = null;
}
+ return pendingRequest;
}
@Override // from AbstractRemoteService
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index fa73758..7bfed91 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -380,7 +380,7 @@
// don't want the navigation bar background be moving around when resizing in docked mode.
// However, we need it for the transitions into/out of docked mode.
if (mNavigationBarColor != null && fullscreen) {
- DecorView.getNavigationBarRect(width, height, stableInsets, systemInsets, mTmpRect);
+ DecorView.getNavigationBarRect(width, height, stableInsets, systemInsets, mTmpRect, 1f);
mNavigationBarColor.setBounds(mTmpRect);
mNavigationBarColor.draw(canvas);
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 723f161..afe7954 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1080,10 +1080,13 @@
}
public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
- Rect contentInsets, Rect outRect) {
- final int bottomInset = getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom);
- final int leftInset = getColorViewLeftInset(stableInsets.left, contentInsets.left);
- final int rightInset = getColorViewLeftInset(stableInsets.right, contentInsets.right);
+ Rect contentInsets, Rect outRect, float scale) {
+ final int bottomInset =
+ (int) (getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom) * scale);
+ final int leftInset =
+ (int) (getColorViewLeftInset(stableInsets.left, contentInsets.left) * scale);
+ final int rightInset =
+ (int) (getColorViewLeftInset(stableInsets.right, contentInsets.right) * scale);
final int size = getNavBarSize(bottomInset, rightInset, leftInset);
if (isNavBarToRightEdge(bottomInset, rightInset)) {
outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 4874c41..4b6a6de 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2380,6 +2380,11 @@
// Note: Only shows up on first time toggle
DIALOG_DARK_UI_INFO = 1740;
+ // OPEN: Settings > About phone > Legal information > Google Play system update licenses
+ // CATEGORY: SETTINGS
+ // OS: Q
+ MODULE_LICENSES_DASHBOARD = 1746;
+
// OPEN: Settings > System > Gestures > Global Actions Panel
// CATEGORY: SETTINGS
// OS: Q
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index a7d4734..7fb6f98 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -591,6 +591,8 @@
SHOWING_UI = 13;
NOT_VISIBLE = 14;
DEAD = 15;
+ NOT_PERCEPTIBLE = 16;
+ INCLUDE_CAPABILITIES = 17;
}
repeated Flag flags = 3;
optional string service_name = 4;
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 3149c2d..432c101 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -308,7 +308,7 @@
<string name="permgrouprequest_storage" msgid="7885942926944299560">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى الصور والوسائط والملفات على جهازك؟"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"الميكروفون"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"تسجيل الصوت"</string>
- <string name="permgrouprequest_microphone" msgid="9167492350681916038">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بتسجيل الصوت؟"</string>
+ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"هل تريد السماح لـ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بتسجيل الصوت؟"</string>
<string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"النشاط البدني"</string>
<string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"الوصول إلى بيانات نشاطك البدني"</string>
<string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"هل تريد السماح للتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى بيانات نشاطك البدني؟"</string>
@@ -320,7 +320,7 @@
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى سجلّ مكالماتك الهاتفية؟"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"الهاتف"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"إجراء مكالمات هاتفية وإدارتها"</string>
- <string name="permgrouprequest_phone" msgid="9166979577750581037">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بإجراء المكالمات الهاتفية وإدارتها؟"</string>
+ <string name="permgrouprequest_phone" msgid="9166979577750581037">"هل تريد السماح لـ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بإجراء المكالمات الهاتفية وإدارتها؟"</string>
<string name="permgrouplab_sensors" msgid="4838614103153567532">"أجهزة استشعار الجسم"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"الوصول إلى بيانات المستشعر حول علاماتك الحيوية"</string>
<string name="permgrouprequest_sensors" msgid="6349806962814556786">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالدخول إلى بيانات المستشعر حول علاماتك الحيوية؟"</string>
@@ -2122,7 +2122,7 @@
<string name="harmful_app_warning_title" msgid="8982527462829423432">"تم العثور على تطبيق ضار"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"يريد تطبيق <xliff:g id="APP_0">%1$s</xliff:g> عرض شرائح تطبيق <xliff:g id="APP_2">%2$s</xliff:g>."</string>
<string name="screenshot_edit" msgid="7867478911006447565">"تعديل"</string>
- <string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"سيهتز الهاتف عند تلقي المكالمات والإشعارات"</string>
+ <string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"سيهتز الهاتف عند تلقّي المكالمات والإشعارات"</string>
<string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"سيتم كتم صوت الهاتف عند تلقي المكالمات والإشعارات"</string>
<string name="notification_channel_system_changes" msgid="5072715579030948646">"تغييرات النظام"</string>
<string name="notification_channel_do_not_disturb" msgid="6766940333105743037">"عدم الإزعاج"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index df5c303..6f50a81 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -131,8 +131,7 @@
<!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
<skip />
<string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> ৱাই- ফাই কলিং"</string>
- <!-- no translation found for wfcSpnFormat_spn_wifi_calling_vo_hyphen (1730997175789582756) -->
- <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="1730997175789582756">"<xliff:g id="SPN">%s</xliff:g> ৱাই-ফাই কলিং"</string>
<string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN কল"</string>
<string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN কল"</string>
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> ৱাই-ফাই"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 0c957e0..135e4e5 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -42,11 +42,11 @@
<string name="serviceErased" msgid="1288584695297200972">"پاک کردن با موفقیت انجام شد."</string>
<string name="passwordIncorrect" msgid="7612208839450128715">"گذرواژه اشتباه است."</string>
<string name="mmiComplete" msgid="8232527495411698359">"MMI کامل شد."</string>
- <string name="badPin" msgid="9015277645546710014">"پین قدیمی که نوشتهاید صحیح نیست."</string>
+ <string name="badPin" msgid="9015277645546710014">"این پین قدیمی که نوشتید صحیح نیست."</string>
<string name="badPuk" msgid="5487257647081132201">"PUK که نوشتهاید صحیح نیست."</string>
<string name="mismatchPin" msgid="609379054496863419">"پینهایی که وارد کردهاید با یکدیگر مطابقت ندارند."</string>
- <string name="invalidPin" msgid="3850018445187475377">"یک پین بنویسید که 4 تا 8 رقم باشد."</string>
- <string name="invalidPuk" msgid="8761456210898036513">"یک PUK با 8 رقم یا بیشتر تایپ کنید."</string>
+ <string name="invalidPin" msgid="3850018445187475377">"یک پین بنویسید که ۴ تا ۸ رقم باشد."</string>
+ <string name="invalidPuk" msgid="8761456210898036513">"یک PUK با ۸ رقم یا بیشتر تایپ کنید."</string>
<string name="needPuk" msgid="919668385956251611">"سیم کارت شما با PUK قفل شده است. کد PUK را برای بازگشایی آن بنویسید."</string>
<string name="needPuk2" msgid="4526033371987193070">"PUK2 را برای بازگشایی قفل سیم کارت بنویسید."</string>
<string name="enablePin" msgid="209412020907207950">"ناموفق بود، قفل سیم/RUIM را فعال کنید."</string>
@@ -73,8 +73,8 @@
<string name="DndMmi" msgid="1265478932418334331">"مزاحم نشوید"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"پیشفرض شناسه تماسگیرنده روی محدود است. تماس بعدی: محدود"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"پیشفرض شناسه تماسگیرنده روی محدود است. تماس بعدی: بدون محدودیت"</string>
- <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"پیشفرض شناسه تماسگیرنده روی غیر محدود است. تماس بعدی: محدود"</string>
- <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"پیشفرض شناسه تماسگیرنده روی غیر محدود است. تماس بعدی: بدون محدودیت"</string>
+ <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"پیشفرض شناسه تماسگیرنده روی غیرمحدود است. تماس بعدی: محدود"</string>
+ <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"پیشفرض شناسه تماسگیرنده روی غیرمحدود است. تماس بعدی: بدون محدودیت"</string>
<string name="serviceNotProvisioned" msgid="8614830180508686666">"سرویس دارای مجوز نیست."</string>
<string name="CLIRPermanent" msgid="3377371145926835671">"شما میتوانید تنظیم شناسه تماسگیرنده را تغییر دهید."</string>
<string name="RestrictedOnDataTitle" msgid="5221736429761078014">"بدون سرویس داده تلفن همراه"</string>
@@ -103,7 +103,7 @@
<string name="serviceClassData" msgid="872456782077937893">"داده"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"نمابر"</string>
<string name="serviceClassSMS" msgid="2015460373701527489">"پیامک"</string>
- <string name="serviceClassDataAsync" msgid="4523454783498551468">"غیر همگام"</string>
+ <string name="serviceClassDataAsync" msgid="4523454783498551468">"ناهمگام"</string>
<string name="serviceClassDataSync" msgid="7530000519646054776">"همگامسازی"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"بسته"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
@@ -154,7 +154,7 @@
<string name="fcError" msgid="3327560126588500777">"مشکل در اتصال یا کد ویژگی نامعتبر."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"تأیید"</string>
<string name="httpError" msgid="7956392511146698522">"خطایی در شبکه وجود داشت."</string>
- <string name="httpErrorLookup" msgid="4711687456111963163">"URL پیدا نشد."</string>
+ <string name="httpErrorLookup" msgid="4711687456111963163">"نشانی اینترنتی پیدا نشد."</string>
<string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"طرح کلی احراز هویت سایت پشتیبانی نمیشود."</string>
<string name="httpErrorAuth" msgid="1435065629438044534">"راستیآزمایی ناموفق بود."</string>
<string name="httpErrorProxyAuth" msgid="1788207010559081331">"احراز هویت از طریق سرور پروکسی انجام نشد."</string>
@@ -164,10 +164,10 @@
<string name="httpErrorRedirectLoop" msgid="8679596090392779516">"این صفحه دارای تعداد بسیار زیادی تغییر مسیر سرور است."</string>
<string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"پروتکل پشتیبانی نمیشود."</string>
<string name="httpErrorFailedSslHandshake" msgid="96549606000658641">"اتصال امن ایجاد نشد."</string>
- <string name="httpErrorBadUrl" msgid="3636929722728881972">"بدلیل نامعتبر بودن URL، باز کردن صفحه ممکن نیست."</string>
+ <string name="httpErrorBadUrl" msgid="3636929722728881972">"بهدلیل نامعتبر بودن نشانی اینترنتی، صفحه باز نمیشود."</string>
<string name="httpErrorFile" msgid="2170788515052558676">"دسترسی به فایل انجام نشد."</string>
<string name="httpErrorFileNotFound" msgid="6203856612042655084">"فایل درخواستی پیدا نشد."</string>
- <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"درخواستهای زیادی در حال پردازش است. بعداً دوباره امتحان کنید."</string>
+ <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"درخواستهای زیادی درحال پردازش است. بعداً دوباره امتحان کنید."</string>
<string name="notification_title" msgid="8967710025036163822">"خطای ورود به سیستم برای <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"همگامسازی"</string>
<string name="contentServiceSyncNotificationTitle" msgid="7036196943673524858">"همگامسازی نشد"</string>
@@ -210,7 +210,7 @@
<string name="reboot_to_update_reboot" msgid="6428441000951565185">"در حال راهاندازی مجدد…"</string>
<string name="reboot_to_reset_title" msgid="4142355915340627490">"بازنشانی دادههای کارخانه"</string>
<string name="reboot_to_reset_message" msgid="2432077491101416345">"در حال راهاندازی مجدد…"</string>
- <string name="shutdown_progress" msgid="2281079257329981203">"در حال خاموش شدن…"</string>
+ <string name="shutdown_progress" msgid="2281079257329981203">"درحال خاموش شدن…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"رایانهٔ لوحی شما خاموش میشود."</string>
<string name="shutdown_confirm" product="tv" msgid="476672373995075359">"تلویزیون شما خاموش خواهد شد."</string>
<string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"ساعت شما خاموش میشود."</string>
@@ -399,7 +399,7 @@
<string name="permlab_readCallLog" msgid="3478133184624102739">"خواندن گزارش تماس"</string>
<string name="permdesc_readCallLog" msgid="3204122446463552146">"این برنامه میتواند سابقه تماس شما را بخواند."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"نوشتن گزارش تماس"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"به برنامه اجازه میدهد گزارشات تماس رایانهٔ لوحی شما، از جمله دادههایی درمورد تماسهای ورودی و خروجی را تغییر دهد. برنامههای مخرب ممکن است از این ویژگی برای پاک کردن یا تغییر گزارش تماس شما استفاده کنند."</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"به برنامه اجازه میدهد گزارشهای تماس رایانهٔ لوحی شما، از جمله دادههایی درباره تماسهای ورودی و خروجی را تغییر دهد. برنامههای مخرب ممکن است از این ویژگی برای پاک کردن یا تغییر گزارش تماس شما استفاده کنند."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"به برنامه اجازه میدهد گزارشات تماس تلویزیون شما، از جمله دادههایی درمورد تماسهای ورودی و خروجی را تغییر دهد. برنامههای مخرب شاید از این ویژگی برای پاک کردن یا تغییر گزارش تماس شما استفاده کنند."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"به برنامه اجازه میدهد گزارشات تماس تلفنی شما، از جمله دادههایی درمورد تماسهای ورودی و خروجی را تغییر دهد. برنامههای مخرب ممکن است از این ویژگی برای پاک کردن یا تغییر گزارش تماس شما استفاده کنند."</string>
<string name="permlab_bodySensors" msgid="4683341291818520277">"دسترسی به حسگرهای بدن (مانند پایشگرهای ضربان قلب)"</string>
@@ -501,7 +501,7 @@
<string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"به برنامه اجازه میدهد تا پیکربندی بلوتوث را در تلویزیون مشاهده کند و اتصالات را با دستگاههای مرتبطشده ایجاد کند و بپذیرد."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"به برنامه اجازه میدهد تا پیکربندی بلوتوث در تلفن را مشاهده کند، و اتصالات دستگاههای مرتبط را برقرار کرده و بپذیرد."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"کنترل ارتباط راه نزدیک"</string>
- <string name="permdesc_nfc" msgid="7120611819401789907">"به برنامه اجازه میدهد تا با تگهای ارتباط میدان نزدیک (NFC)، کارتها و فایل خوان ارتباط برقرار کند."</string>
+ <string name="permdesc_nfc" msgid="7120611819401789907">"به برنامه اجازه میدهد تا با تگهای «ارتباط میدان نزدیک» (NFC)، کارتها و فایلخوان ارتباط برقرار کند."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"غیرفعال کردن قفل صفحه شما"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"به برنامه امکان میدهد قفل کلید و هر گونه امنیت گذرواژه مرتبط را غیرفعال کند. بهعنوان مثال تلفن هنگام دریافت یک تماس تلفنی ورودی قفل کلید را غیرفعال میکند و بعد از پایان تماس، قفل کلید را دوباره فعال میکند."</string>
<string name="permlab_requestPasswordComplexity" msgid="202650535669249674">"درخواست پیچیدگی قفل صفحه"</string>
@@ -656,7 +656,7 @@
<string name="policylab_watchLogin" msgid="5091404125971980158">"پایش تلاشهای باز کردن قفل صفحه"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"تعداد گذرواژههای نادرست تایپ شده را هنگام بازکردن قفل صفحه کنترل میکند، و اگر دفعات زیادی گذرواژه نادرست وارد شود رایانهٔ لوحی را قفل میکند و همه دادههای رایانهٔ لوحی را پاک میکند."</string>
<string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"بر تعداد گذرواژههای نادرست تایپشده در زمان باز کردن قفل صفحه نظارت کنید و اگر تعدا زیادی گذرواژههای اشتباه تایپ شده است، تلویزیون را قفل کنید یا همه دادههای تلویزیون را پاک کنید."</string>
- <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"تعداد گذرواژههای نادرست تایپ شده را هنگام بازکردن قفل صفحه کنترل میکند. اگر دفعات زیادی گذرواژه نادرست وارد شود، تلفن را قفل میکند یا همه دادههای تلفن را پاک میکند."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"تعداد گذرواژههای نادرست تایپشده را هنگام بازکردن قفل صفحه کنترل میکند و اگر چندین بار گذرواژههای نادرست وارد شود، تلفن را قفل میکند یا همه دادههای تلفن را پاک میکند."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"بر تعداد گذرواژههای نادرستی که هنگام باز کردن قفل صفحه تایپ شده، نظارت میکند، و اگر تعداد گذرواژههای تایپ شده نادرست بیش از حد بود، رایانه لوحی را قفل میکند یا کلیه دادههای کاربر را پاک میکند."</string>
<string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"بر تعداد گذرواژههای نادرستی که هنگام باز کردن قفل صفحه تایپ شده، نظارت میکند، و اگر تعداد گذرواژههای تایپ شده نادرست بیش از حد بود، تلویزیون را قفل میکند یا کلیه دادههای کاربر را پاک میکند."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"بر تعداد گذرواژههای نادرستی که هنگام باز کردن قفل صفحه تایپ شده، نظارت میکند، و اگر تعداد گذرواژههای تایپ شده نادرست بیش از حد بود، تلفن را قفل میکند یا کلیه دادههای کاربر را پاک میکند."</string>
@@ -678,7 +678,7 @@
<string name="policydesc_expirePassword" msgid="5367525762204416046">"تغییر تعداد دفعاتی که گذرواژه، پین یا الگوی قفل صفحه باید تغییر کند."</string>
<string name="policylab_encryptedStorage" msgid="8901326199909132915">"تنظیم رمزگذاری حافظه"</string>
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"اطلاعات ذخیره شده برنامه باید رمزگذاری شود."</string>
- <string name="policylab_disableCamera" msgid="6395301023152297826">"غیر فعال کردن دوربین ها"</string>
+ <string name="policylab_disableCamera" msgid="6395301023152297826">"غیرفعال کردن دوربینها"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"جلوگیری از استفاده از همه دوربینهای دستگاه."</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"غیرفعال کردن ویژگیهای قفل صفحه"</string>
<string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"مانع استفاده از برخی ویژگیهای قفل صفحه میشود."</string>
@@ -838,7 +838,7 @@
<string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"سیم کارت با PUK قفل شده است."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"لطفاً به راهنمای کاربر مراجعه کرده یا با مرکز پشتیبانی از مشتریان تماس بگیرید."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"سیم کارت قفل شد."</string>
- <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"بازگشایی قفل سیم کارت..."</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"بازگشایی قفل سیم کارت…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"گذرواژهٔ خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردهاید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"پین را<xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردهاید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
@@ -850,7 +850,7 @@
<string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"شما به اشتباه <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. پس از<xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، تلفن به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"شما به اشتباه اقدام به باز کردن قفل <xliff:g id="NUMBER">%d</xliff:g> رایانهٔ لوحی کردهاید. رایانهٔ لوحی در حال حاضر به پیشفرض کارخانه بازنشانی میشود."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"<xliff:g id="NUMBER">%d</xliff:g> دفعه به صورت نادرست سعی کردهاید قفل تلویزیون را باز کنید. اکنون تلویزیون به تنظیمات پیشفرض کارخانه بازنشانی خواهد شد."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"شما به اشتباه <xliff:g id="NUMBER">%d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. این تلفن در حال حاضر به پیشفرض کارخانه بازنشانی میشود."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"بهاشتباه <xliff:g id="NUMBER">%d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. این تلفن دیگر به پیشفرض کارخانه بازنشانی میشود."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"پس از <xliff:g id="NUMBER">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"الگو را فراموش کردهاید؟"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"بازگشایی قفل حساب"</string>
@@ -861,7 +861,7 @@
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ورود به سیستم"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"نام کاربر یا گذرواژه نامعتبر است."</string>
<string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"نام کاربری یا گذرواژهٔ خود را فراموش کردید؟\nاز "<b>"google.com/accounts/recovery"</b>" بازدید کنید."</string>
- <string name="lockscreen_glogin_checking_password" msgid="7114627351286933867">"در حال بررسی..."</string>
+ <string name="lockscreen_glogin_checking_password" msgid="7114627351286933867">"درحال بررسی…"</string>
<string name="lockscreen_unlock_label" msgid="737440483220667054">"بازگشایی قفل"</string>
<string name="lockscreen_sound_on_label" msgid="9068877576513425970">"صدا روشن"</string>
<string name="lockscreen_sound_off_label" msgid="996822825154319026">"صدا خاموش"</string>
@@ -1077,7 +1077,7 @@
<string name="failed_to_copy_to_clipboard" msgid="1833662432489814471">"در بریدهدان کپی نشد"</string>
<string name="paste" msgid="5629880836805036433">"جایگذاری"</string>
<string name="paste_as_plain_text" msgid="5427792741908010675">"جایگذاری به عنوان متن ساده"</string>
- <string name="replace" msgid="5781686059063148930">"جایگزین شود..."</string>
+ <string name="replace" msgid="5781686059063148930">"جایگزین شود…"</string>
<string name="delete" msgid="6098684844021697789">"حذف"</string>
<string name="copyUrl" msgid="2538211579596067402">"کپی URL"</string>
<string name="selectTextMode" msgid="1018691815143165326">"انتخاب متن"</string>
@@ -1121,7 +1121,7 @@
<string name="yes" msgid="5362982303337969312">"تأیید"</string>
<string name="no" msgid="5141531044935541497">"لغو"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"توجه"</string>
- <string name="loading" msgid="7933681260296021180">"در حال بارکردن…"</string>
+ <string name="loading" msgid="7933681260296021180">"درحال بارکردن…"</string>
<string name="capital_on" msgid="1544682755514494298">"روشن"</string>
<string name="capital_off" msgid="6815870386972805832">"خاموش"</string>
<string name="whichApplication" msgid="4533185947064773386">"تکمیل عملکرد با استفاده از"</string>
@@ -1198,8 +1198,8 @@
<string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g> درحال ارتقا است...."</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"در حال بهینهسازی برنامهٔ <xliff:g id="NUMBER_0">%1$d</xliff:g> از <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"آمادهسازی <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
- <string name="android_upgrading_starting_apps" msgid="451464516346926713">"در حال آغاز برنامهها."</string>
- <string name="android_upgrading_complete" msgid="1405954754112999229">"در حال اتمام راهاندازی."</string>
+ <string name="android_upgrading_starting_apps" msgid="451464516346926713">"درحال آغاز کردن برنامهها."</string>
+ <string name="android_upgrading_complete" msgid="1405954754112999229">"درحال اتمام راهاندازی."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> در حال اجرا"</string>
<string name="heavy_weight_notification_detail" msgid="2304833848484424985">"برای برگشت به بازی، ضربه بزنید"</string>
<string name="heavy_weight_switcher_title" msgid="387882830435195342">"انتخاب بازی"</string>
@@ -1306,8 +1306,8 @@
<string name="wifi_p2p_frequency_conflict_message" product="tv" msgid="3087858235069421128">"در حالی که تلویزیون به <xliff:g id="DEVICE_NAME">%1$s</xliff:g> متصل است، ارتباط آن به صورت موقت از Wi-Fi قطع خواهد شد."</string>
<string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"این گوشی بهطور موقت از Wi-Fi قطع خواهد شد، در حالی که به <xliff:g id="DEVICE_NAME">%1$s</xliff:g> وصل است"</string>
<string name="select_character" msgid="3365550120617701745">"درج نویسه"</string>
- <string name="sms_control_title" msgid="7296612781128917719">"ارسال پیامک ها"</string>
- <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> در حال ارسال تعداد زیادی پیامک است. آیا اجازه میدهید این برنامه همچنان پیامک ارسال کند؟"</string>
+ <string name="sms_control_title" msgid="7296612781128917719">"درحال ارسال پیامکها"</string>
+ <string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> درحال ارسال تعداد زیادی پیامک است. آیا اجازه میدهید این برنامه همچنان پیامک ارسال کند؟"</string>
<string name="sms_control_yes" msgid="3663725993855816807">"مجاز است"</string>
<string name="sms_control_no" msgid="625438561395534982">"اجازه ندارد"</string>
<string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> مایل است پیامی به <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> ارسال کند."</string>
@@ -1459,7 +1459,7 @@
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"ارائهدهنده وضعیت"</string>
<string name="notification_ranker_binding_label" msgid="774540592299064747">"سرویس رتبهبندی اعلان"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN فعال شد"</string>
- <string name="vpn_title_long" msgid="6400714798049252294">"VPN توسط <xliff:g id="APP">%s</xliff:g> فعال شده است"</string>
+ <string name="vpn_title_long" msgid="6400714798049252294">"VPN را <xliff:g id="APP">%s</xliff:g> فعال کرده است"</string>
<string name="vpn_text" msgid="1610714069627824309">"برای مدیریت شبکه ضربه بزنید."</string>
<string name="vpn_text_long" msgid="4907843483284977618">"به <xliff:g id="SESSION">%s</xliff:g> متصل شد. برای مدیریت شبکه ضربه بزنید."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"در حال اتصال VPN همیشه فعال…"</string>
@@ -1495,11 +1495,11 @@
<string name="find_previous" msgid="2196723669388360506">"یافتن قبلی"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"درخواست مکان از <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="gpsNotifTitle" msgid="5446858717157416839">"درخواست مکان"</string>
- <string name="gpsNotifMessage" msgid="1374718023224000702">"درخواست شده توسط <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
+ <string name="gpsNotifMessage" msgid="1374718023224000702">"درخواستکننده <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"بله"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"نه"</string>
<string name="sync_too_many_deletes" msgid="5296321850662746890">"از حد مجاز حذف فراتر رفت"</string>
- <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"<xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> مورد حذف شده برای <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>، حساب <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> وجود دارد. میخواهید چه کاری انجام دهید؟"</string>
+ <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"<xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> مورد حذفشده برای <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>، حساب <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> وجود دارد. میخواهید چه کار بکنید؟"</string>
<string name="sync_really_delete" msgid="2572600103122596243">"حذف موارد"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"واگرد موارد حذف شده"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"اکنون کاری انجام نشود"</string>
@@ -1564,11 +1564,11 @@
<string name="data_usage_rapid_app_body" msgid="5396680996784142544">"<xliff:g id="APP">%s</xliff:g> بیش از معمول داده مصرف کرده است"</string>
<string name="ssl_certificate" msgid="6510040486049237639">"گواهی امنیتی"</string>
<string name="ssl_certificate_is_valid" msgid="6825263250774569373">"این گواهی معتبر است."</string>
- <string name="issued_to" msgid="454239480274921032">"صادر شده برای:"</string>
+ <string name="issued_to" msgid="454239480274921032">"صادرشده برای:"</string>
<string name="common_name" msgid="2233209299434172646">"نام معمولی:"</string>
<string name="org_name" msgid="6973561190762085236">"سازمان:"</string>
<string name="org_unit" msgid="7265981890422070383">"واحد سازمانی:"</string>
- <string name="issued_by" msgid="2647584988057481566">"صادر شده توسط:"</string>
+ <string name="issued_by" msgid="2647584988057481566">"صادرکننده:"</string>
<string name="validity_period" msgid="8818886137545983110">"اعتبار:"</string>
<string name="issued_on" msgid="5895017404361397232">"صادر شده در:"</string>
<string name="expires_on" msgid="3676242949915959821">"تاریخ انقضا:"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index da5f866..c8482f9 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -308,7 +308,7 @@
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à vos journaux d\'appels?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Téléphone"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"faire et gérer des appels téléphoniques"</string>
- <string name="permgrouprequest_phone" msgid="9166979577750581037">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à faire et à gérer les appels téléphoniques?"</string>
+ <string name="permgrouprequest_phone" msgid="9166979577750581037">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à faire et à gérer des appels téléphoniques?"</string>
<string name="permgrouplab_sensors" msgid="4838614103153567532">"Capteurs corporels"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"accéder aux données des capteurs sur vos signes vitaux"</string>
<string name="permgrouprequest_sensors" msgid="6349806962814556786">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder aux données des capteurs pour vos signes vitaux?"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 4e9b0d2..d05a184 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -116,7 +116,7 @@
<string name="roamingText6" msgid="2059440825782871513">"रोमिंग - उपलब्ध सिस्टम"</string>
<string name="roamingText7" msgid="7112078724097233605">"रोमिंग - गठबंधन सहयोगी"</string>
<string name="roamingText8" msgid="5989569778604089291">"रोमिंग - प्रीमियम सहयोगी"</string>
- <string name="roamingText9" msgid="7969296811355152491">"रोमिंग - पूर्ण सेवा काम की क्षमता"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"रोमिंग - पूरी सेवा काम की क्षमता"</string>
<string name="roamingText10" msgid="3992906999815316417">"रोमिंग - आंशिक सेवा काम की क्षमता"</string>
<string name="roamingText11" msgid="4154476854426920970">"रोमिंग बैनर चालू"</string>
<string name="roamingText12" msgid="1189071119992726320">"रोमिंग बैनर बंद"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 69c53db..ef6397a 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -902,7 +902,7 @@
<string name="granularity_label_link" msgid="5815508880782488267">"링크"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"행"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"출고 테스트 불합격"</string>
- <string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST 작업은 /system/app 디렉토리에 설치된 패키지에 대해서만 지원됩니다."</string>
+ <string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST 작업은 /system/app 디렉터리에 설치된 패키지에 대해서만 지원됩니다."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TEST 작업을 제공하는 패키지가 없습니다."</string>
<string name="factorytest_reboot" msgid="6320168203050791643">"다시 부팅"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"\'<xliff:g id="TITLE">%s</xliff:g>\' 페이지 내용:"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index b760e28..d8dde58 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -296,7 +296,7 @@
<string name="permgrouprequest_storage" msgid="7885942926944299560">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား သင့်ဖုန်းရှိ ဓာတ်ပုံများ၊ မီဒီယာနှင့် ဖိုင်များ ဝင်သုံးခွင့်ပေးလိုပါသလား။"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"မိုက်ခရိုဖုန်း"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"အသံဖမ်းခြင်း"</string>
- <string name="permgrouprequest_microphone" msgid="9167492350681916038">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား အသံဖမ်းယူခွင့် ပေးလိုပါသလား။"</string>
+ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ကို အသံဖမ်းယူခွင့် ပေးလိုပါသလား။"</string>
<string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"ကိုယ်လက်လှုပ်ရှားမှု"</string>
<string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"သင့်ကိုယ်လက်လှုပ်ရှားမှုကို ဝင်ကြည့်ရန်"</string>
<string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား သင့်ကိုယ်လက်လှုပ်ရှားမှုကို ဝင်ကြည့်ခွင့် ပေးလိုပါသလား။"</string>
@@ -308,7 +308,7 @@
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား သင်၏ခေါ်ဆိုထားသော မှတ်တမ်းများကို သုံးခွင့်ပေးလိုပါသလား။"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"ဖုန်း"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"ဖုန်းခေါ်ဆိုမှုများ ပြုလုပ်ရန်နှင့် စီမံရန်"</string>
- <string name="permgrouprequest_phone" msgid="9166979577750581037">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား ဖုန်းခေါ်ဆိုမှုများ ပြုလုပ်ခွင့်နှင့် စီမံခွင့်ပေးလိုပါသလား။"</string>
+ <string name="permgrouprequest_phone" msgid="9166979577750581037">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ကို ဖုန်းခေါ်ဆိုမှုများ ပြုလုပ်ခွင့်နှင့် စီမံခွင့်ပေးလိုပါသလား။"</string>
<string name="permgrouplab_sensors" msgid="4838614103153567532">"စက်၏ အာရုံခံစနစ်များ"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"သင်၏ အဓိကကျသော လက္ခဏာများအကြောင်း အာရုံခံကိရိယာဒေတာကို ရယူသုံးစွဲရန်"</string>
<string name="permgrouprequest_sensors" msgid="6349806962814556786">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား သင်၏ အရေးကြီးသောလက္ခဏာ အာရုံခံကိရိယာ ဒေတာများကို သုံးခွင့်ပေးလိုပါသလား။"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 6e2ba99a..c48668a 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -308,7 +308,7 @@
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"Ungependa kuiruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ifikie rekodi zako za nambari za simu?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Simu"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"piga na udhibiti simu"</string>
- <string name="permgrouprequest_phone" msgid="9166979577750581037">"Ungependa kuiruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ipige na kudhibiti simu?"</string>
+ <string name="permgrouprequest_phone" msgid="9166979577750581037">"Ungependa kuruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> kupiga na kudhibiti simu?"</string>
<string name="permgrouplab_sensors" msgid="4838614103153567532">"Vihisi vya mwili"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"fikia data ya kitambuzi kuhusu alama zako muhimu"</string>
<string name="permgrouprequest_sensors" msgid="6349806962814556786">"Ungependa kuiruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ifikie data ya vitambuzi kuhusu viashiria muhimu vya mwili wako?"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 30001b1..5e150c5 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -131,8 +131,7 @@
<!-- no translation found for wfcSpnFormat_spn (4998685024207291232) -->
<skip />
<string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> வைஃபை அழைப்பு"</string>
- <!-- no translation found for wfcSpnFormat_spn_wifi_calling_vo_hyphen (1730997175789582756) -->
- <skip />
+ <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="1730997175789582756">"<xliff:g id="SPN">%s</xliff:g> வைஃபை அழைப்பு"</string>
<string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN அழைப்பு"</string>
<string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN அழைப்பு"</string>
<string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> வைஃபை"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 14abb77..d6604f4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -364,7 +364,7 @@
overridden by the device to present the capability of creating socket keepalives. -->
<!-- An Array of "[NetworkCapabilities.TRANSPORT_*],[supported keepalives] -->
<string-array translatable="false" name="config_networkSupportedKeepaliveCount">
- <item>0,3</item>
+ <item>0,1</item>
<item>1,3</item>
</string-array>
@@ -2787,6 +2787,9 @@
screen. -->
<bool name="config_lowRamTaskSnapshotsAndRecents">false</bool>
+ <!-- The amount to scale fullscreen snapshots for Overview and snapshot starting windows. -->
+ <item name="config_fullTaskSnapshotScale" format="float" type="dimen">1.0</item>
+
<!-- Determines whether recent tasks are provided to the user. Default device has recents
property. If this is false, then the following recents config flags are ignored. -->
<bool name="config_hasRecents">true</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9d75654..50814c5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -355,6 +355,7 @@
<java-symbol type="bool" name="config_enableMultiUserUI"/>
<java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
+ <java-symbol type="dimen" name="config_fullTaskSnapshotScale" />
<java-symbol type="bool" name="config_lowRamTaskSnapshotsAndRecents" />
<java-symbol type="bool" name="config_hasRecents" />
<java-symbol type="string" name="config_recentsComponentName" />
diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
index 0f32a82..6161108 100644
--- a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
+++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
@@ -24,6 +24,7 @@
import android.app.ActivityManager;
import android.app.Notification;
+import android.app.Person;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -87,6 +88,9 @@
assertEquals(0,
logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_SUMMARY));
assertNull(logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CATEGORY));
+ assertNull(logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_STYLE));
+ assertNull(logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_PEOPLE));
+
}
/** Verify that modifying the returned logMaker won't leave stale data behind for
@@ -159,6 +163,24 @@
sbn.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
}
+ @Test
+ public void testLogMakerWithPerson() {
+ Notification.Builder builder = getNotificationBuilder(GROUP_ID_1, CHANNEL_ID)
+ .addPerson(new Person.Builder().build());
+ final LogMaker logMaker = getNotification(PKG, builder).getLogMaker();
+ assertEquals(1,
+ logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_PEOPLE));
+ }
+
+ @Test
+ public void testLogMakerWithStyle() {
+ Notification.Builder builder = getNotificationBuilder(GROUP_ID_1, CHANNEL_ID)
+ .setStyle(new Notification.MessagingStyle(new Person.Builder().build()));
+ final LogMaker logMaker = getNotification(PKG, builder).getLogMaker();
+ assertEquals("android.app.Notification$MessagingStyle".hashCode(),
+ logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_STYLE));
+ }
+
private StatusBarNotification getNotification(String pkg, String group, String channelId) {
return getNotification(pkg, getNotificationBuilder(group, channelId));
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c023e85..985eeee 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -187,6 +187,7 @@
<permission name="android.permission.ACCESS_CACHE_FILESYSTEM"/>
<permission name="android.permission.CLEAR_APP_CACHE"/>
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+ <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
</privapp-permissions>
diff --git a/graphics/java/android/graphics/drawable/ColorStateListDrawable.java b/graphics/java/android/graphics/drawable/ColorStateListDrawable.java
index 35021a6..20cd825 100644
--- a/graphics/java/android/graphics/drawable/ColorStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorStateListDrawable.java
@@ -25,6 +25,7 @@
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.util.MathUtils;
/**
@@ -136,6 +137,12 @@
}
@Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mColorDrawable.setBounds(bounds);
+ }
+
+ @Override
protected boolean onStateChange(int[] state) {
if (mState.mColor != null) {
int color = mState.mColor.getColorForState(state, mState.mColor.getDefaultColor());
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index f3a1b0e..760d554 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -139,9 +139,12 @@
final ChildDrawable[] r = new ChildDrawable[length];
for (int i = 0; i < length; i++) {
r[i] = new ChildDrawable(mLayerState.mDensity);
- r[i].mDrawable = layers[i];
- layers[i].setCallback(this);
- mLayerState.mChildrenChangingConfigurations |= layers[i].getChangingConfigurations();
+ Drawable child = layers[i];
+ r[i].mDrawable = child;
+ if (child != null) {
+ child.setCallback(this);
+ mLayerState.mChildrenChangingConfigurations |= child.getChangingConfigurations();
+ }
}
mLayerState.mNumChildren = length;
mLayerState.mChildren = r;
@@ -416,7 +419,8 @@
final ChildDrawable[] layers = mLayerState.mChildren;
final int N = mLayerState.mNumChildren;
for (int i = 0; i < N; i++) {
- if (layers[i].mDrawable.isProjected()) {
+ Drawable childDrawable = layers[i].mDrawable;
+ if (childDrawable != null && childDrawable.isProjected()) {
return true;
}
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 150b6f9..8d420e2 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -211,21 +211,22 @@
}
void JMediaCodec::release() {
- if (mCodec != NULL) {
- mCodec->release();
- mCodec.clear();
- mInitStatus = NO_INIT;
- }
+ std::call_once(mReleaseFlag, [this] {
+ if (mCodec != NULL) {
+ mCodec->release();
+ mInitStatus = NO_INIT;
+ }
- if (mLooper != NULL) {
- mLooper->unregisterHandler(id());
- mLooper->stop();
- mLooper.clear();
- }
+ if (mLooper != NULL) {
+ mLooper->unregisterHandler(id());
+ mLooper->stop();
+ mLooper.clear();
+ }
+ });
}
JMediaCodec::~JMediaCodec() {
- if (mCodec != NULL || mLooper != NULL) {
+ if (mLooper != NULL) {
/* MediaCodec and looper should have been released explicitly already
* in setMediaCodec() (see comments in setMediaCodec()).
*
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index de08550..dfe30a3 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -17,6 +17,8 @@
#ifndef _ANDROID_MEDIA_MEDIACODEC_H_
#define _ANDROID_MEDIA_MEDIACODEC_H_
+#include <mutex>
+
#include "jni.h"
#include <media/MediaAnalyticsItem.h>
@@ -156,6 +158,7 @@
sp<ALooper> mLooper;
sp<MediaCodec> mCodec;
AString mNameAtCreation;
+ std::once_flag mReleaseFlag;
sp<AMessage> mCallbackNotification;
sp<AMessage> mOnFrameRenderedNotification;
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index d946fbc..467c4a4 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -29,6 +29,9 @@
<bool name="config_enableRightNavigationBar">false</bool>
<bool name="config_enableBottomNavigationBar">true</bool>
+ <!-- Whether heads-up notifications should be shown when shade is open. -->
+ <bool name="config_enableHeadsUpNotificationWhenNotificationShadeOpen">true</bool>
+
<bool name="config_hideNavWhenKeyguardBouncerShown">true</bool>
<bool name="config_enablePersistentDockedActivity">false</bool>
<string name="config_persistentDockedActivityIntentUri" translatable="false"></string>
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 16b0125..a07bb8f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -165,6 +165,8 @@
private boolean mIsNotificationCardSwiping;
// If notification shade is being swiped vertically to close.
private boolean mIsSwipingVerticallyToClose;
+ // Whether heads-up notifications should be shown when shade is open.
+ private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
private final CarPowerStateListener mCarPowerStateListener =
(int state) -> {
@@ -459,6 +461,8 @@
}
});
+ mEnableHeadsUpNotificationWhenNotificationShadeOpen = mContext.getResources().getBoolean(
+ R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
CarHeadsUpNotificationManager carHeadsUpNotificationManager =
new CarSystemUIHeadsUpNotificationManager(mContext,
mNotificationClickHandlerFactory, mNotificationDataManager);
@@ -1273,11 +1277,18 @@
}
@Override
+ protected void setInternalInsetsInfo(ViewTreeObserver.InternalInsetsInfo info,
+ HeadsUpEntry currentNotification, boolean panelExpanded) {
+ super.setInternalInsetsInfo(info, currentNotification, mPanelExpanded);
+ }
+
+ @Override
protected void setHeadsUpVisible() {
// if the Notifications panel is showing don't show the Heads up
- if (mPanelExpanded) {
+ if (!mEnableHeadsUpNotificationWhenNotificationShadeOpen && mPanelExpanded) {
return;
}
+
super.setHeadsUpVisible();
if (mHeadsUpPanel.getVisibility() == View.VISIBLE) {
mStatusBarWindowController.setHeadsUpShowing(true);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index ab174f4..f16fb1c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -712,11 +712,25 @@
public boolean matches(WifiConfiguration config) {
if (config.isPasspoint()) {
return (isPasspoint() && config.FQDN.equals(mConfig.FQDN));
- } else {
- // Normal non-Passpoint network
- return ssid.equals(removeDoubleQuotes(config.SSID))
- && security == getSecurity(config)
- && (mConfig == null || mConfig.shared == config.shared);
+ }
+
+ if (!ssid.equals(removeDoubleQuotes(config.SSID))
+ || (mConfig != null && mConfig.shared != config.shared)) {
+ return false;
+ }
+
+ final int configSecurity = getSecurity(config);
+ final WifiManager wifiManager = getWifiManager();
+ switch (security) {
+ case SECURITY_PSK_SAE_TRANSITION:
+ return configSecurity == SECURITY_PSK
+ || (wifiManager.isWpa3SaeSupported() && configSecurity == SECURITY_SAE);
+ case SECURITY_OWE_TRANSITION:
+ return configSecurity == SECURITY_NONE
+ || (wifiManager.isEnhancedOpenSupported()
+ && configSecurity == SECURITY_OWE);
+ default:
+ return security == configSecurity;
}
}
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
index eabc5c5..508619a 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
@@ -22,6 +22,7 @@
android:focusable="true"
android:clickable="true"
>
+
<com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundNormal"
android:layout_width="match_parent"
@@ -38,28 +39,7 @@
android:gravity="center_vertical"
android:orientation="horizontal"
>
- <TextView
- android:id="@+id/header_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginStart="@dimen/notification_section_header_padding_left"
- android:gravity="start"
- android:textAlignment="gravity"
- android:text="@string/notification_section_header_gentle"
- android:textSize="12sp"
- android:textColor="@color/notification_section_header_label_color"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- />
- <ImageView
- android:id="@+id/btn_clear_all"
- android:layout_width="@dimen/notification_section_header_height"
- android:layout_height="@dimen/notification_section_header_height"
- android:layout_marginEnd="4dp"
- android:src="@drawable/status_bar_notification_section_header_clear_btn"
- android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
- android:scaleType="center"
- />
+ <include layout="@layout/status_bar_notification_section_header_contents"/>
</LinearLayout>
<com.android.systemui.statusbar.notification.FakeShadowView
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml
new file mode 100644
index 0000000..feabd1c
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml
@@ -0,0 +1,41 @@
+<!--
+ ~ 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
+ -->
+
+<!-- Used by both status_bar_notification_header and SectionHeaderView -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android" >
+ <TextView
+ android:id="@+id/header_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/notification_section_header_padding_left"
+ android:gravity="start"
+ android:textAlignment="gravity"
+ android:text="@string/notification_section_header_gentle"
+ android:textSize="12sp"
+ android:textColor="@color/notification_section_header_label_color"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ />
+ <ImageView
+ android:id="@+id/btn_clear_all"
+ android:layout_width="@dimen/notification_section_header_height"
+ android:layout_height="@dimen/notification_section_header_height"
+ android:layout_marginEnd="4dp"
+ android:src="@drawable/status_bar_notification_section_header_clear_btn"
+ android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
+ android:scaleType="center"
+ />
+</merge>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 0612acb..212763a 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -664,7 +664,7 @@
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Тут канфігурыраваць гэту групу апавяшчэнняў забаронена"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Праксіраванае апавяшчэнне"</string>
<string name="notification_channel_dialog_title" msgid="5745335243729167866">"Усе апавяшчэнні праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
- <string name="see_more_title" msgid="5358726697042112726">"Разгарнуць"</string>
+ <string name="see_more_title" msgid="5358726697042112726">"Яшчэ"</string>
<string name="appops_camera" msgid="8100147441602585776">"Гэта праграма выкарыстоўвае камеру."</string>
<string name="appops_microphone" msgid="741508267659494555">"Гэта праграма выкарыстоўвае мікрафон."</string>
<string name="appops_overlay" msgid="6165912637560323464">"Гэта праграма паказваецца на экране паверх іншых праграм."</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index da625fa..4200b43 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -451,8 +451,8 @@
<string name="battery_saver_notification_title" msgid="8614079794522291840">"S\'ha activat l\'estalvi de bateria"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"Redueix el rendiment i l\'ús de les dades en segon pla."</string>
<string name="battery_saver_notification_action_text" msgid="132118784269455533">"Desactiva l\'estalvi de bateria"</string>
- <string name="media_projection_dialog_text" msgid="8585357687598538511">"Quan graves o emets contingut, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> pot capturar la informació sensible que es mostri a la pantalla o que reprodueixi el dispositiu, com ara àudio, contrasenyes, informació de pagament, fotos i missatges."</string>
- <string name="media_projection_dialog_service_text" msgid="3075544489835858258">"Quan graves o emets contingut, el servei que ofereix aquesta funció pot capturar informació sensible que es mostri a la pantalla o que reprodueixi el dispositiu, com ara àudio, contrasenyes, informació de pagament, fotos i missatges."</string>
+ <string name="media_projection_dialog_text" msgid="8585357687598538511">"Quan graves o emets contingut, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> pot capturar la informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu, com ara àudio, contrasenyes, informació de pagament, fotos i missatges."</string>
+ <string name="media_projection_dialog_service_text" msgid="3075544489835858258">"Quan graves o emets contingut, el servei que ofereix aquesta funció pot capturar informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu, com ara àudio, contrasenyes, informació de pagament, fotos i missatges."</string>
<string name="media_projection_dialog_title" msgid="8124184308671641248">"Es mostra informació sensible durant l\'emissió o la gravació"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"No ho tornis a mostrar"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Esborra-ho tot"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 5861873..773ac74 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -197,7 +197,7 @@
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Probíhá změna sítě operátora"</string>
<string name="accessibility_battery_details" msgid="7645516654955025422">"Otevřít podrobnosti o baterii"</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"Stav baterie: <xliff:g id="NUMBER">%d</xliff:g> procent."</string>
- <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Baterie je nabitá na <xliff:g id="PERCENTAGE">%1$s</xliff:g> %, při vašem používání vydrží ještě <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Baterie je nabitá na <xliff:g id="PERCENTAGE">%1$s</xliff:g> procent, při vašem používání vydrží ještě <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Baterie se nabíjí. Nabito: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%"</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Systémová nastavení."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Oznámení."</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index f495c43..4e8bd52 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -199,7 +199,7 @@
<!-- String.format failed for translation -->
<!-- no translation found for accessibility_battery_level (7451474187113371965) -->
<skip />
- <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Akku bei <xliff:g id="PERCENTAGE">%1$s</xliff:g> %, bei deinem Nutzungsmuster hast du noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Akku bei <xliff:g id="PERCENTAGE">%1$s</xliff:g> Prozent. Bei deinem Nutzungsmuster hast du noch Strom für etwa <xliff:g id="TIME">%2$s</xliff:g>"</string>
<!-- String.format failed for translation -->
<!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
<skip />
@@ -234,7 +234,7 @@
<string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"Der Flugmodus ist deaktiviert."</string>
<string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Der Flugmodus ist aktiviert."</string>
<string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"lautlos"</string>
- <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"nur Wecker"</string>
+ <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"nur Weckrufe"</string>
<string name="accessibility_quick_settings_dnd" msgid="5555155552520665891">"Nicht stören."</string>
<string name="accessibility_quick_settings_dnd_changed_off" msgid="2757071272328547807">"\"Bitte nicht stören\" deaktiviert."</string>
<string name="accessibility_quick_settings_dnd_changed_on" msgid="6808220653747701059">"\"Bitte nicht stören\" aktiviert"</string>
@@ -304,7 +304,7 @@
<string name="quick_settings_header_onboarding_text" msgid="8030309023792936283">"Halte die Symbole gedrückt, um weitere Optionen zu sehen"</string>
<string name="quick_settings_dnd_label" msgid="7112342227663678739">"Bitte nicht stören"</string>
<string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"Nur wichtige Unterbrechungen"</string>
- <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Nur Wecker"</string>
+ <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Nur Weckrufe"</string>
<string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"Lautlos"</string>
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> Geräte)"</string>
@@ -412,7 +412,7 @@
<string name="interruption_level_none_with_warning" msgid="5114872171614161084">"Lautlos. Damit werden auch Screenreader stummgeschaltet."</string>
<string name="interruption_level_none" msgid="6000083681244492992">"Lautlos"</string>
<string name="interruption_level_priority" msgid="6426766465363855505">"Nur wichtige Unterbrechungen"</string>
- <string name="interruption_level_alarms" msgid="5226306993448328896">"Nur Wecker"</string>
+ <string name="interruption_level_alarms" msgid="5226306993448328896">"Nur Weckrufe"</string>
<string name="interruption_level_none_twoline" msgid="3957581548190765889">"Laut-\nlos"</string>
<string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Nur\nwichtige"</string>
<string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Nur\nWecker"</string>
@@ -596,7 +596,7 @@
<string name="enable_demo_mode" msgid="4844205668718636518">"Demomodus aktivieren"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Demomodus anzeigen"</string>
<string name="status_bar_ethernet" msgid="5044290963549500128">"Ethernet"</string>
- <string name="status_bar_alarm" msgid="8536256753575881818">"Wecker"</string>
+ <string name="status_bar_alarm" msgid="8536256753575881818">"Weckruf"</string>
<string name="status_bar_work" msgid="6022553324802866373">"Arbeitsprofil"</string>
<string name="status_bar_airplane" msgid="7057575501472249002">"Flugmodus"</string>
<string name="add_tile" msgid="2995389510240786221">"Kachel hinzufügen"</string>
@@ -823,7 +823,7 @@
<string name="accessibility_quick_settings_settings" msgid="6132460890024942157">"Einstellungen öffnen."</string>
<string name="accessibility_quick_settings_expand" msgid="2375165227880477530">"Schnelleinstellungen öffnen."</string>
<string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"Schnelleinstellungen schließen."</string>
- <string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"Wecker eingestellt."</string>
+ <string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"Weckruf eingerichtet."</string>
<string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Angemeldet als <xliff:g id="ID_1">%s</xliff:g>"</string>
<string name="data_connection_no_internet" msgid="4503302451650972989">"Kein Internet"</string>
<string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Details öffnen."</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index a6bd968..981e0cf 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -451,9 +451,9 @@
<string name="battery_saver_notification_title" msgid="8614079794522291840">"Ahorro de batería activado"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"Reduce el rendimiento y los datos en segundo plano"</string>
<string name="battery_saver_notification_action_text" msgid="132118784269455533">"Desactivar Ahorro de batería"</string>
- <string name="media_projection_dialog_text" msgid="8585357687598538511">"Mientras graba o envía contenido, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede obtener información sensible que se muestre en la pantalla o que se reproduzca en el dispositivo, como audio, contraseñas, información de pagos, fotos y mensajes."</string>
- <string name="media_projection_dialog_service_text" msgid="3075544489835858258">"Mientras graba o envía contenido, el servicio que ofrece esta función puede obtener información sensible que se muestre en la pantalla o que se reproduzca en el dispositivo, como audio, contraseñas, información de pagos, fotos y mensajes."</string>
- <string name="media_projection_dialog_title" msgid="8124184308671641248">"Se muestra información sensible durante el envío y la grabación"</string>
+ <string name="media_projection_dialog_text" msgid="8585357687598538511">"Mientras grabas o envías contenido, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede capturar información sensible que se muestre en la pantalla o que se reproduzca en el dispositivo, como audio, contraseñas, información de pagos, fotos y mensajes."</string>
+ <string name="media_projection_dialog_service_text" msgid="3075544489835858258">"Mientras grabas o envías contenido, el servicio que ofrece esta función puede capturar información sensible que se muestre en la pantalla o que se reproduzca en el dispositivo, como audio, contraseñas, información de pagos, fotos y mensajes."</string>
+ <string name="media_projection_dialog_title" msgid="8124184308671641248">"Sobre información sensible durante el envío y la grabación"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"No volver a mostrar"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Borrar todo"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Gestionar"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 542ec43..f9c0c9c 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -923,7 +923,7 @@
<string name="bubbles_prompt" msgid="8807968030159469710">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren burbuilak erabiltzeko baimena eman nahi duzu?"</string>
<string name="manage_bubbles_text" msgid="7027739766859191408">"Kudeatu"</string>
<string name="no_bubbles" msgid="337101288173078247">"Ukatu"</string>
- <string name="yes_bubbles" msgid="668809525728633841">"Onartu"</string>
+ <string name="yes_bubbles" msgid="668809525728633841">"Baimendu"</string>
<string name="ask_me_later_bubbles" msgid="2147688438402939029">"Galdetu geroago"</string>
<string name="bubble_content_description_single" msgid="1184462974339387516">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
<string name="bubble_content_description_stack" msgid="8666349184095622232">"<xliff:g id="APP_NAME">%2$s</xliff:g> aplikazioaren \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" jakinarazpena, eta beste <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 02e017e..8d51525 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -70,7 +70,7 @@
<string name="compat_mode_off" msgid="4434467572461327898">"گسترده کردن برای پر کردن صفحه"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"عکس صفحهنمایش"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"در حال ذخیره عکس صفحهنمایش..."</string>
- <string name="screenshot_saving_title" msgid="8242282144535555697">"در حال ذخیره عکس صفحهنمایش..."</string>
+ <string name="screenshot_saving_title" msgid="8242282144535555697">"درحال ذخیره عکس صفحهنمایش…"</string>
<string name="screenshot_saved_title" msgid="5637073968117370753">"عکس صفحهنمایش ذخیره شد"</string>
<string name="screenshot_saved_text" msgid="7574667448002050363">"برای مشاهده عکس صفحهنمایشتان ضربه بزنید"</string>
<string name="screenshot_failed_title" msgid="7612509838919089748">"عکس صفحهنمایش ذخیره نشد"</string>
@@ -151,9 +151,9 @@
<string name="accessibility_bluetooth_name" msgid="8441517146585531676">"به <xliff:g id="BLUETOOTH">%s</xliff:g> متصل شد."</string>
<string name="accessibility_cast_name" msgid="4026393061247081201">"متصل به <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_no_wimax" msgid="4329180129727630368">"WiMAX وجود ندارد."</string>
- <string name="accessibility_wimax_one_bar" msgid="4170994299011863648">"WiMAX دارای یک نوار است."</string>
- <string name="accessibility_wimax_two_bars" msgid="9176236858336502288">"WiMAX دارای دو نوار است."</string>
- <string name="accessibility_wimax_three_bars" msgid="6116551636752103927">"WiMAX دارای سه نوار است."</string>
+ <string name="accessibility_wimax_one_bar" msgid="4170994299011863648">"WiMAX یک نوار دارد."</string>
+ <string name="accessibility_wimax_two_bars" msgid="9176236858336502288">"WiMAX دو نوار دارد."</string>
+ <string name="accessibility_wimax_three_bars" msgid="6116551636752103927">"WiMAX سه نوار دارد."</string>
<string name="accessibility_wimax_signal_full" msgid="2768089986795579558">"قدرت سیگنال WiMAX کامل است."</string>
<string name="accessibility_ethernet_disconnected" msgid="5896059303377589469">"اترنت قطع شد."</string>
<string name="accessibility_ethernet_connected" msgid="2692130313069182636">"اترنت متصل شد."</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 85a89ef..a21c8e2 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -197,7 +197,7 @@
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Operaattorin verkko muuttuu"</string>
<string name="accessibility_battery_details" msgid="7645516654955025422">"Avaa akun tiedot."</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"Akun virta <xliff:g id="NUMBER">%d</xliff:g> prosenttia."</string>
- <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Akkua jäljellä <xliff:g id="PERCENTAGE">%1$s</xliff:g> % eli noin <xliff:g id="TIME">%2$s</xliff:g> käyttösi perusteella"</string>
+ <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Akkua jäljellä <xliff:g id="PERCENTAGE">%1$s</xliff:g> prosenttia eli noin <xliff:g id="TIME">%2$s</xliff:g> käyttösi perusteella"</string>
<string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Akku latautuu: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> prosenttia"</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Järjestelmän asetukset"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Ilmoitukset"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index ae288c9..70cde3e 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -197,7 +197,7 @@
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Changer de réseau de fournisseur de services"</string>
<string name="accessibility_battery_details" msgid="7645516654955025422">"Ouvrir les détails de la pile"</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"Pile : <xliff:g id="NUMBER">%d</xliff:g> pour cent"</string>
- <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Pile chargée à <xliff:g id="PERCENTAGE">%1$s</xliff:g> % (environ <xliff:g id="TIME">%2$s</xliff:g> d\'autonomie en fonction de votre usage)"</string>
+ <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Pile chargée à <xliff:g id="PERCENTAGE">%1$s</xliff:g> pour cent (environ <xliff:g id="TIME">%2$s</xliff:g> d\'autonomie en fonction de votre usage)"</string>
<string name="accessibility_battery_level_charging" msgid="1147587904439319646">"La pile est en cours de charge : <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Paramètres système"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notifications"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 4aedbcd..5c4d71e 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -197,7 +197,7 @@
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Modification du réseau de l\'opérateur"</string>
<string name="accessibility_battery_details" msgid="7645516654955025422">"Ouvrir les détails de la batterie"</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"Batterie : <xliff:g id="NUMBER">%d</xliff:g> pour cent"</string>
- <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> pour cent de batterie : il reste environ <xliff:g id="TIME">%2$s</xliff:g>, en fonction de votre utilisation"</string>
+ <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Batterie chargée à <xliff:g id="PERCENTAGE">%1$s</xliff:g> pour cent : il reste environ <xliff:g id="TIME">%2$s</xliff:g> d\'autonomie, selon votre utilisation"</string>
<string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batterie en charge : <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Paramètres système"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notifications"</string>
@@ -451,9 +451,9 @@
<string name="battery_saver_notification_title" msgid="8614079794522291840">"Économiseur de batterie activé"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"Limite les performances et les données en arrière-plan."</string>
<string name="battery_saver_notification_action_text" msgid="132118784269455533">"Désactiver l\'économiseur de batterie"</string>
- <string name="media_projection_dialog_text" msgid="8585357687598538511">"Pendant que vous enregistrez ou diffusez du contenu, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> peut capturer des informations sensibles affichées à l\'écran ou lues par votre appareil, y compris des contenus audio, des mots de passe, des informations de paiement, des photos et des messages."</string>
+ <string name="media_projection_dialog_text" msgid="8585357687598538511">"Pendant que vous enregistrez ou diffusez du contenu, l\'appli <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> peut capturer des informations sensibles affichées à l\'écran ou lues par votre appareil, y compris des contenus audio, des mots de passe, des informations de paiement, des photos et des messages."</string>
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"Pendant que vous enregistrez ou diffusez du contenu, le service concerné peut capturer des informations sensibles affichées à l\'écran ou lues par votre appareil, y compris des contenus audio, des mots de passe, des informations de paiement, des photos et des messages."</string>
- <string name="media_projection_dialog_title" msgid="8124184308671641248">"Exposition d\'informations sensibles lors de l\'enregistrement ou de la diffusion de contenu"</string>
+ <string name="media_projection_dialog_title" msgid="8124184308671641248">"Présence d\'informations sensibles lors de l\'enregistrement ou de la diffusion de contenu"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ne plus afficher"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Tout effacer"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Gérer"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 9bbb170..a425615 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -197,7 +197,7 @@
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Cambio de rede do operador"</string>
<string name="accessibility_battery_details" msgid="7645516654955025422">"Abrir os detalles da batería"</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"Carga da batería: <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
- <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Batería: <xliff:g id="PERCENTAGE">%1$s</xliff:g> %, durará <xliff:g id="TIME">%2$s</xliff:g> co uso que adoitas darlle"</string>
+ <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Batería: <xliff:g id="PERCENTAGE">%1$s</xliff:g> por cento, durará <xliff:g id="TIME">%2$s</xliff:g> co uso que adoitas darlle"</string>
<string name="accessibility_battery_level_charging" msgid="1147587904439319646">"A batería está cargando. Nivel: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Configuración do sistema"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificacións"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index bd160e7..18f4781 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -197,7 +197,7 @@
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Skiptir um farsímakerfi"</string>
<string name="accessibility_battery_details" msgid="7645516654955025422">"Opna upplýsingar um rafhlöðu"</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"<xliff:g id="NUMBER">%d</xliff:g> prósent á rafhlöðu."</string>
- <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Rafhlaða í <xliff:g id="PERCENTAGE">%1$s</xliff:g>%, um það bil <xliff:g id="TIME">%2$s</xliff:g> eftir miðað við notkun þína"</string>
+ <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Rafhlaða í <xliff:g id="PERCENTAGE">%1$s</xliff:g> prósentum, um það bil <xliff:g id="TIME">%2$s</xliff:g> eftir miðað við notkun þína"</string>
<string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Rafhlaða í hleðslu, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Kerfisstillingar."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Tilkynningar."</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index bdb03bc..a04f239 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -875,7 +875,7 @@
<string name="instant_apps_message_with_help" msgid="6179830437630729747">"Қолданба орнатылмай-ақ ашылды. Толығырақ мәлімет алу үшін түртіңіз."</string>
<string name="app_info" msgid="6856026610594615344">"Қолданба ақпараты"</string>
<string name="go_to_web" msgid="2650669128861626071">"Браузерге өту"</string>
- <string name="mobile_data" msgid="7094582042819250762">"Мобильдік деректер"</string>
+ <string name="mobile_data" msgid="7094582042819250762">"Мобильдік интернет"</string>
<string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="mobile_carrier_text_format" msgid="3241721038678469804">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi өшірулі"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 13a0f1f..5b0a1ea 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -451,9 +451,9 @@
<string name="battery_saver_notification_title" msgid="8614079794522291840">"កម្មវិធីសន្សំថ្មបានបើក"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"ការបន្ថយការប្រតិបត្តិ និងទិន្នន័យផ្ទៃខាងក្រោយ"</string>
<string name="battery_saver_notification_action_text" msgid="132118784269455533">"បិទកម្មវិធីសន្សំថ្ម"</string>
- <string name="media_projection_dialog_text" msgid="8585357687598538511">"នៅពេលកំពុងថត ឬបញ្ជូន <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> អាចថតព័ត៌មានរសើបទាំងឡាយដែលបង្ហាញនៅលើអេក្រង់របស់អ្នក ឬដែលចាក់ពីឧបករណ៍របស់អ្នក រួមទាំងព័ត៌មានរសើបដូចជា សំឡេង ពាក្យសម្ងាត់ ព័ត៌មានបង់ប្រាក់ រូបថត និងសារជាដើម។"</string>
+ <string name="media_projection_dialog_text" msgid="8585357687598538511">"នៅពេលកំពុងថត ឬភ្ជាប់ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> អាចថតព័ត៌មានរសើបទាំងឡាយដែលបង្ហាញនៅលើអេក្រង់របស់អ្នក ឬដែលចាក់ពីឧបករណ៍របស់អ្នក រួមទាំងព័ត៌មានរសើបដូចជា សំឡេង ពាក្យសម្ងាត់ ព័ត៌មានបង់ប្រាក់ រូបថត និងសារជាដើម។"</string>
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"នៅពេលកំពុងថត ឬបញ្ជូន សេវាកម្មដែលផ្ដល់មុខងារនេះអាចថតព័ត៌មានរសើបទាំងឡាយដែលបង្ហាញនៅលើអេក្រង់របស់អ្នក ឬដែលចាក់ពីឧបករណ៍របស់អ្នក រួមទាំងព័ត៌មានរសើបដូចជា សំឡេង ពាក្យសម្ងាត់ ព័ត៌មានបង់ប្រាក់ រូបថត និងសារជាដើម។"</string>
- <string name="media_projection_dialog_title" msgid="8124184308671641248">"បង្ហាញព័ត៌មានរសើប អំឡុងពេលបញ្ជូន/ថត"</string>
+ <string name="media_projection_dialog_title" msgid="8124184308671641248">"ការបង្ហាញព័ត៌មានរសើប អំឡុងពេលភ្ជាប់/ថត"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"កុំបង្ហាញម្ដងទៀត"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"សម្អាតទាំងអស់"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"គ្រប់គ្រង"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 7367fa5..caa07faf9 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -451,7 +451,7 @@
<string name="battery_saver_notification_title" msgid="8614079794522291840">"ബാറ്ററി ലാഭിക്കൽ ഓണാണ്"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"പ്രവർത്തനവും പശ്ചാത്തല ഡാറ്റയും കുറയ്ക്കുന്നു"</string>
<string name="battery_saver_notification_action_text" msgid="132118784269455533">"ബാറ്ററി ലാഭിക്കൽ ഓഫാക്കുക"</string>
- <string name="media_projection_dialog_text" msgid="8585357687598538511">"റെക്കോർഡ് അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യുന്നതിനിടെ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> നിങ്ങളുടെ സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്തതോ ആയ ഓഡിയോ, പാസ്വേഡുകൾ, പേയ്മെന്റ് വിവരം, ഫോട്ടോകൾ, സന്ദേശങ്ങൾ എന്നിവ ഉൾപ്പെടെയുള്ള തന്ത്രപ്രധാന വിവരങ്ങൾ ക്യാപ്ചർ ചെയ്യാനാവും."</string>
+ <string name="media_projection_dialog_text" msgid="8585357687598538511">"റെക്കോർഡ് അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യുന്നതിനിടെ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് നിങ്ങളുടെ സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്തതോ ആയ ഓഡിയോ, പാസ്വേഡുകൾ, പേയ്മെന്റ് വിവരം, ഫോട്ടോകൾ, സന്ദേശങ്ങൾ എന്നിവ ഉൾപ്പെടെയുള്ള തന്ത്രപ്രധാന വിവരങ്ങൾ ക്യാപ്ചർ ചെയ്യാനാവും."</string>
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"റെക്കോർഡ് അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യുന്നതിനിടെ, ഈ പ്രവർത്തനത്തിനാവശ്യമായ സേവനത്തിന്, നിങ്ങളുടെ സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്തതോ ആയ ഓഡിയോ, പാസ്വേഡുകൾ, പേയ്മെന്റ് വിവരം, ഫോട്ടോകൾ, സന്ദേശങ്ങൾ എന്നിവ ഉൾപ്പെടെയുള്ള തന്ത്രപ്രധാന വിവരങ്ങൾ ക്യാപ്ചർ ചെയ്യാനാവും."</string>
<string name="media_projection_dialog_title" msgid="8124184308671641248">"കാസ്റ്റ്/റെക്കോർഡ് ചെയ്യുമ്പോൾ സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട വിവരം വെളിപ്പെടുത്തുന്നു"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"വീണ്ടും കാണിക്കരുത്"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 650f35e..adbbb64 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -451,7 +451,7 @@
<string name="battery_saver_notification_title" msgid="8614079794522291840">"ဘက်ထရီ အားထိန်းကို ဖွင့်ထားခြင်း"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"လုပ်ကိုင်မှုကို လျှော့ချလျက် နောက်ခံ ဒေတာကို ကန့်သတ်သည်"</string>
<string name="battery_saver_notification_action_text" msgid="132118784269455533">"ဘက်ထရီ အားထိန်းကို ပိတ်ရန်"</string>
- <string name="media_projection_dialog_text" msgid="8585357687598538511">"အသံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် အသံ၊ စကားဝှက်၊ ငွေပေးချေမှုဆိုင်ရာ အချက်အလက်၊ ဓာတ်ပုံနှင့် မက်ဆေ့ဂျ်များကဲ့သို့ အရေးကြီးသည့် အချက်အလက်များအပါအဝင် သင့်မျက်နှာပြင်တွင် ပြသထားသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အရေးကြီးသည့် အချက်အလက်မှန်သမျှကို ဖမ်းယူနိုင်ပါသည်။"</string>
+ <string name="media_projection_dialog_text" msgid="8585357687598538511">"အသံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် အသံ၊ စကားဝှက်၊ ငွေပေးချေမှုဆိုင်ရာ အချက်အလက်၊ ဓာတ်ပုံနှင့် မက်ဆေ့ဂျ်များကဲ့သို့ အရေးကြီးသည့် အချက်အလက်များအပါအဝင် ဖန်သားပြင်တွင် ပြသထားသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အရေးကြီးသည့် အချက်အလက်မှန်သမျှကို ဖမ်းယူနိုင်ပါသည်။"</string>
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"အသံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် ဤလုပ်ဆောင်ချက်ကို ပေးအပ်သည့် ဝန်ဆောင်မှုသည် အသံ၊ စကားဝှက်၊ ငွေပေးချေမှုဆိုင်ရာ အချက်အလက်၊ ဓာတ်ပုံနှင့် မက်ဆေ့ဂျ်များကဲ့သို့ အရေးကြီးသည့် အချက်အလက်များအပါအဝင် သင့်မျက်နှာပြင်တွင် ပြသထားသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အရေးကြီးသည့် အချက်အလက်မှန်သမျှကို ဖမ်းယူနိုင်ပါသည်။"</string>
<string name="media_projection_dialog_title" msgid="8124184308671641248">"ကာစ်လုပ်နေစဉ်/အသံဖမ်းနေစဉ် အရေးကြီးသောအချက်အလက်များ ထုတ်ဖော်မိခြင်း"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"နောက်ထပ် မပြပါနှင့်"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 29c423a..df437b3 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -459,7 +459,7 @@
<string name="battery_saver_notification_title" msgid="8614079794522291840">"Oszczędzanie baterii jest włączone"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"Zmniejsza wydajność i ogranicza dane w tle"</string>
<string name="battery_saver_notification_action_text" msgid="132118784269455533">"Wyłącz Oszczędzanie baterii"</string>
- <string name="media_projection_dialog_text" msgid="8585357687598538511">"Podczas nagrywania lub przesyłania aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> może rejestrować wszelkie informacje poufne wyświetlane na ekranie lub odtwarzane na urządzeniu takie jak dźwięki czy podawane hasła, informacje o płatnościach, zdjęcia i wiadomości."</string>
+ <string name="media_projection_dialog_text" msgid="8585357687598538511">"Podczas nagrywania lub przesyłania aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> może rejestrować wszelkie informacje poufne wyświetlane na ekranie lub odtwarzane na urządzeniu, takie jak dźwięki czy podawane hasła, informacje o płatnościach, zdjęcia i wiadomości."</string>
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"Podczas nagrywania lub przesyłania usługa udostępniająca tę funkcję może rejestrować wszelkie informacje poufne wyświetlane na ekranie lub odtwarzane na urządzeniu takie jak dźwięki czy podawane hasła, informacje o płatnościach, zdjęcia i wiadomości."</string>
<string name="media_projection_dialog_title" msgid="8124184308671641248">"Ujawnianie poufnych informacji podczas przesyłania/nagrywania"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Nie pokazuj ponownie"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index c03c108..f5ed50f 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -197,7 +197,7 @@
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Сменить сеть"</string>
<string name="accessibility_battery_details" msgid="7645516654955025422">"Сведения о расходе заряда батареи"</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"Заряд батареи в процентах: <xliff:g id="NUMBER">%d</xliff:g>."</string>
- <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Заряд батареи – <xliff:g id="PERCENTAGE">%1$s</xliff:g> %. При текущем уровне расхода его хватит примерно на такое время: <xliff:g id="TIME">%2$s</xliff:g>."</string>
+ <string name="accessibility_battery_level_with_estimate" msgid="9033100930684311630">"Заряд батареи в процентах: <xliff:g id="PERCENTAGE">%1$s</xliff:g>. Оценка оставшегося времени работы: <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Зарядка батареи. Текущий заряд: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Настройки"</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Уведомления"</string>
@@ -457,7 +457,7 @@
<string name="battery_saver_notification_title" msgid="8614079794522291840">"Режим энергосбережения включен"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"Откл. фоновой передачи данных"</string>
<string name="battery_saver_notification_action_text" msgid="132118784269455533">"Отключить режим энергосбережения"</string>
- <string name="media_projection_dialog_text" msgid="8585357687598538511">"При записи сообщений или трансляции экрана приложение \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" может получить доступ к конфиденциальной информации, которая отображается на экране или воспроизводится на устройстве, например к аудиозаписям, паролям, фото, сообщениям и платежным данным."</string>
+ <string name="media_projection_dialog_text" msgid="8585357687598538511">"При записи сообщений или трансляции экрана приложение <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> может получить доступ к конфиденциальной информации, которая отображается на экране или воспроизводится на устройстве, например к аудиозаписям, паролям, фото, сообщениям и платежным данным."</string>
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"При записи сообщений или трансляции экрана сервис может получить доступ к конфиденциальной информации, которая отображается на экране или воспроизводится на устройстве, например к аудиозаписям, паролям, фото, сообщениям и платежным данным."</string>
<string name="media_projection_dialog_title" msgid="8124184308671641248">"Раскрытие личной информации при записи или трансляции"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Больше не показывать"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 6a9d3d3..0366303 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -453,7 +453,7 @@
<string name="battery_saver_notification_action_text" msgid="132118784269455533">"Inaktivera batterisparläget"</string>
<string name="media_projection_dialog_text" msgid="8585357687598538511">"När du spelar in eller castar kan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> registrera vilka känsliga uppgifter som helst som visas på skärmen eller spelas upp på enheten, inklusive ljud, lösenord, betalningsuppgifter, foton och meddelanden."</string>
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"När du spelar in eller castar kan tjänsten som tillhandahåller funktionen registrera vilka känsliga uppgifter som helst som visas på skärmen eller spelas upp på enheten, inklusive ljud, lösenord, betalningsuppgifter, foton och meddelanden."</string>
- <string name="media_projection_dialog_title" msgid="8124184308671641248">"Avslöja känsliga uppgifter under inspelning och vid castning"</string>
+ <string name="media_projection_dialog_title" msgid="8124184308671641248">"Känsliga uppgifters synlighet under inspelning och vid castning"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Visa inte igen"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Rensa alla"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Hantera"</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 77bb514..9228b17 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -55,10 +55,17 @@
/**
* Control the {@param alpha} of the back button in the navigation bar and {@param animate} if
* needed from current value
+ * @deprecated
*/
void setBackButtonAlpha(float alpha, boolean animate) = 8;
/**
+ * Control the {@param alpha} of the option nav bar button (back-button in 2 button mode
+ * and home bar in no-button mode) and {@param animate} if needed from current value
+ */
+ void setNavBarButtonAlpha(float alpha, boolean animate) = 19;
+
+ /**
* Proxies motion events from the homescreen UI to the status bar. Only called when
* swipe down is detected on WORKSPACE. The sender guarantees the following order of events on
* the tracking pointer.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index db13ef4..7fbe5db 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -21,7 +21,9 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import android.annotation.IntDef;
+import android.content.Context;
import android.content.res.Resources;
+import android.view.ViewConfiguration;
import android.view.WindowManagerPolicyConstants;
import com.android.internal.policy.ScreenDecorationsUtils;
@@ -101,6 +103,18 @@
}
/**
+ * Ratio of quickstep touch slop (when system takes over the touch) to view touch slop
+ */
+ public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
+
+ /**
+ * Touch slop for quickstep gesture
+ */
+ public static final float getQuickStepTouchSlopPx(Context context) {
+ return QUICKSTEP_TOUCH_SLOP_RATIO * ViewConfiguration.get(context).getScaledTouchSlop();
+ }
+
+ /**
* Touch slopes and thresholds for quick step operations. Drag slop is the point where the
* home button press/long press over are ignored and will start to drag when exceeded and the
* touch slop is when the respected operation will occur when exceeded. Touch slop must be
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index f66463e..ffa69fa 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -365,6 +365,10 @@
} else {
setPercentTextAtCurrentLevel();
}
+ } else {
+ setContentDescription(
+ getContext().getString(mCharging ? R.string.accessibility_battery_level_charging
+ : R.string.accessibility_battery_level, mLevel));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 909b68b..0af333e 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -57,8 +57,8 @@
private static final String LEARNING_EVENT_COUNT_KEY = "reminder_exp_learning_event_count";
private static final String LEARNED_HINT_LAST_SHOWN_KEY =
"reminder_exp_learned_hint_last_shown";
- private static final long DEFAULT_LEARNING_TIME_MS = TimeUnit.DAYS.toMillis(200);
- private static final int DEFAULT_LEARNING_COUNT = 30000;
+ private static final long DEFAULT_LEARNING_TIME_MS = TimeUnit.DAYS.toMillis(10);
+ private static final int DEFAULT_LEARNING_COUNT = 10;
private static final long DEFAULT_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS = 150;
private static final long DEFAULT_SHOW_AND_GO_DELAYED_LONG_DELAY_MS =
TimeUnit.SECONDS.toMillis(1);
@@ -66,7 +66,7 @@
TimeUnit.SECONDS.toMillis(3);
private static final boolean DEFAULT_SUPPRESS_ON_LOCKSCREEN = false;
private static final boolean DEFAULT_SUPPRESS_ON_LAUNCHER = false;
- private static final boolean DEFAULT_SUPPRESS_ON_APPS = false;
+ private static final boolean DEFAULT_SUPPRESS_ON_APPS = true;
private static final String[] DEFAULT_HOME_CHANGE_ACTIONS = new String[] {
PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED,
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/CircularCornerPathRenderer.java b/packages/SystemUI/src/com/android/systemui/assist/ui/CircularCornerPathRenderer.java
index 162e09e..00346a3 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/CircularCornerPathRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/CircularCornerPathRenderer.java
@@ -53,11 +53,12 @@
break;
case TOP_RIGHT:
mPath.moveTo(mWidth, mCornerRadiusTop);
- mPath.arcTo(mWidth - mCornerRadiusTop, 0, mWidth, mCornerRadiusTop, 0, -90, true);
+ mPath.arcTo(mWidth - mCornerRadiusTop * 2, 0, mWidth, mCornerRadiusTop * 2, 0, -90,
+ true);
break;
case TOP_LEFT:
mPath.moveTo(mCornerRadiusTop, 0);
- mPath.arcTo(0, 0, mCornerRadiusTop, mCornerRadiusTop, 270, -90, true);
+ mPath.arcTo(0, 0, mCornerRadiusTop * 2, mCornerRadiusTop * 2, 270, -90, true);
break;
}
return mPath;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index f8856ce..ae7d142 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -16,9 +16,13 @@
package com.android.systemui.classifier;
+import android.annotation.IntDef;
import android.hardware.SensorEvent;
import android.view.MotionEvent;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* An abstract class for classifiers for touch and sensor events.
*/
@@ -34,6 +38,21 @@
public static final int BOUNCER_UNLOCK = 8;
public static final int PULSE_EXPAND = 9;
+ @IntDef({
+ QUICK_SETTINGS,
+ NOTIFICATION_DISMISS,
+ NOTIFICATION_DRAG_DOWN,
+ NOTIFICATION_DOUBLE_TAP,
+ UNLOCK,
+ LEFT_AFFORDANCE,
+ RIGHT_AFFORDANCE,
+ GENERIC,
+ BOUNCER_UNLOCK,
+ PULSE_EXPAND
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InteractionType {}
+
/**
* Contains all the information about touch events from which the classifier can query
*/
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
new file mode 100644
index 0000000..8c39e9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -0,0 +1,328 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.net.Uri;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.systemui.classifier.Classifier;
+import com.android.systemui.plugins.FalsingManager;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * FalsingManager designed to make clear why a touch was rejected.
+ */
+public class BrightLineFalsingManager implements FalsingManager {
+
+ static final boolean DEBUG = false;
+ private static final String TAG = "FalsingManagerPlugin";
+
+ private final SensorManager mSensorManager;
+ private final FalsingDataProvider mDataProvider;
+ private boolean mSessionStarted;
+
+ private final ExecutorService mBackgroundExecutor = Executors.newSingleThreadExecutor();
+
+ private final List<FalsingClassifier> mClassifiers;
+
+ private SensorEventListener mSensorEventListener = new SensorEventListener() {
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ onSensorEvent(event);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+
+ BrightLineFalsingManager(FalsingDataProvider falsingDataProvider, SensorManager sensorManager) {
+ mDataProvider = falsingDataProvider;
+ mSensorManager = sensorManager;
+ mClassifiers = new ArrayList<>();
+ DistanceClassifier distanceClassifier = new DistanceClassifier(mDataProvider);
+ ProximityClassifier proximityClassifier = new ProximityClassifier(distanceClassifier,
+ mDataProvider);
+ mClassifiers.add(new PointerCountClassifier(mDataProvider));
+ mClassifiers.add(new TypeClassifier(mDataProvider));
+ mClassifiers.add(new DiagonalClassifier(mDataProvider));
+ mClassifiers.add(distanceClassifier);
+ mClassifiers.add(proximityClassifier);
+ mClassifiers.add(new ZigZagClassifier(mDataProvider));
+ }
+
+ private void registerSensors() {
+ Sensor s = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ if (s != null) {
+ // This can be expensive, and doesn't need to happen on the main thread.
+ mBackgroundExecutor.submit(() -> {
+ logDebug("registering sensor listener");
+ mSensorManager.registerListener(
+ mSensorEventListener, s, SensorManager.SENSOR_DELAY_GAME);
+ });
+ }
+ }
+
+
+ private void unregisterSensors() {
+ // This can be expensive, and doesn't need to happen on the main thread.
+ mBackgroundExecutor.submit(() -> {
+ logDebug("unregistering sensor listener");
+ mSensorManager.unregisterListener(mSensorEventListener);
+ });
+ }
+
+ private void sessionStart() {
+ logDebug("Starting Session");
+ mSessionStarted = true;
+ registerSensors();
+ mClassifiers.forEach(FalsingClassifier::onSessionStarted);
+ }
+
+ private void sessionEnd() {
+ if (mSessionStarted) {
+ logDebug("Ending Session");
+ mSessionStarted = false;
+ unregisterSensors();
+ mDataProvider.onSessionEnd();
+ mClassifiers.forEach(FalsingClassifier::onSessionEnded);
+ }
+ }
+
+ private void updateInteractionType(@Classifier.InteractionType int type) {
+ logDebug("InteractionType: " + type);
+ mClassifiers.forEach((classifier) -> classifier.setInteractionType(type));
+ }
+
+ @Override
+ public boolean isClassiferEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isFalseTouch() {
+ boolean r = mClassifiers.stream().anyMatch(falsingClassifier -> {
+ boolean result = falsingClassifier.isFalseTouch();
+ if (result) {
+ logInfo(falsingClassifier.getClass().getName() + ": true");
+ } else {
+ logDebug(falsingClassifier.getClass().getName() + ": false");
+ }
+ return result;
+ });
+
+ logDebug("Is false touch? " + r);
+
+ return r;
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent motionEvent, int width, int height) {
+ // TODO: some of these classifiers might allow us to abort early, meaning we don't have to
+ // make these calls.
+ mDataProvider.onMotionEvent(motionEvent);
+ mClassifiers.forEach((classifier) -> classifier.onTouchEvent(motionEvent));
+ }
+
+ private void onSensorEvent(SensorEvent sensorEvent) {
+ // TODO: some of these classifiers might allow us to abort early, meaning we don't have to
+ // make these calls.
+ mClassifiers.forEach((classifier) -> classifier.onSensorEvent(sensorEvent));
+ }
+
+ @Override
+ public void onSucccessfulUnlock() {
+ }
+
+ @Override
+ public void onNotificationActive() {
+ }
+
+ @Override
+ public void setShowingAod(boolean showingAod) {
+ if (showingAod) {
+ sessionEnd();
+ } else {
+ sessionStart();
+ }
+ }
+
+ @Override
+ public void onNotificatonStartDraggingDown() {
+ updateInteractionType(Classifier.NOTIFICATION_DRAG_DOWN);
+
+ }
+
+ @Override
+ public boolean isUnlockingDisabled() {
+ return false;
+ }
+
+
+ @Override
+ public void onNotificatonStopDraggingDown() {
+ }
+
+ @Override
+ public void setNotificationExpanded() {
+ }
+
+ @Override
+ public void onQsDown() {
+ updateInteractionType(Classifier.QUICK_SETTINGS);
+ }
+
+ @Override
+ public void setQsExpanded(boolean b) {
+ }
+
+ @Override
+ public boolean shouldEnforceBouncer() {
+ return false;
+ }
+
+ @Override
+ public void onTrackingStarted(boolean secure) {
+ updateInteractionType(secure ? Classifier.BOUNCER_UNLOCK : Classifier.UNLOCK);
+ }
+
+ @Override
+ public void onTrackingStopped() {
+ }
+
+ @Override
+ public void onLeftAffordanceOn() {
+ }
+
+ @Override
+ public void onCameraOn() {
+ }
+
+ @Override
+ public void onAffordanceSwipingStarted(boolean rightCorner) {
+ updateInteractionType(
+ rightCorner ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE);
+ }
+
+ @Override
+ public void onAffordanceSwipingAborted() {
+ }
+
+ @Override
+ public void onStartExpandingFromPulse() {
+ updateInteractionType(Classifier.PULSE_EXPAND);
+ }
+
+ @Override
+ public void onExpansionFromPulseStopped() {
+ }
+
+ @Override
+ public Uri reportRejectedTouch() {
+ return null;
+ }
+
+ @Override
+ public void onScreenOnFromTouch() {
+ sessionStart();
+ }
+
+ @Override
+ public boolean isReportingEnabled() {
+ return false;
+ }
+
+ @Override
+ public void onUnlockHintStarted() {
+ }
+
+ @Override
+ public void onCameraHintStarted() {
+ }
+
+ @Override
+ public void onLeftAffordanceHintStarted() {
+ }
+
+ @Override
+ public void onScreenTurningOn() {
+ sessionStart();
+ }
+
+ @Override
+ public void onScreenOff() {
+ sessionEnd();
+ }
+
+
+ @Override
+ public void onNotificatonStopDismissing() {
+ }
+
+ @Override
+ public void onNotificationDismissed() {
+ }
+
+ @Override
+ public void onNotificatonStartDismissing() {
+ updateInteractionType(Classifier.NOTIFICATION_DISMISS);
+ }
+
+ @Override
+ public void onNotificationDoubleTap(boolean b, float v, float v1) {
+ }
+
+ @Override
+ public void onBouncerShown() {
+ }
+
+ @Override
+ public void onBouncerHidden() {
+ }
+
+ @Override
+ public void dump(PrintWriter printWriter) {
+ }
+
+ static void logDebug(String msg) {
+ logDebug(msg, null);
+ }
+
+ static void logDebug(String msg, Throwable throwable) {
+ if (DEBUG) {
+ Log.d(TAG, msg, throwable);
+ }
+ }
+
+ static void logInfo(String msg) {
+ Log.i(TAG, msg);
+ }
+
+ static void logError(String msg) {
+ Log.e(TAG, msg);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java
new file mode 100644
index 0000000..730907e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
+import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
+
+/**
+ * False on swipes that are too close to 45 degrees.
+ *
+ * Horizontal swipes may have a different threshold than vertical.
+ *
+ * This falser should not run on "affordance" swipes, as they will always be close to 45.
+ */
+class DiagonalClassifier extends FalsingClassifier {
+
+ private static final float HORIZONTAL_ANGLE_RANGE = (float) (5f / 360f * Math.PI * 2f);
+ private static final float VERTICAL_ANGLE_RANGE = (float) (5f / 360f * Math.PI * 2f);
+ private static final float DIAGONAL = (float) (Math.PI / 4); // 45 deg
+ private static final float NINETY_DEG = (float) (Math.PI / 2);
+ private static final float ONE_HUNDRED_EIGHTY_DEG = (float) (Math.PI);
+ private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI);
+
+ DiagonalClassifier(FalsingDataProvider dataProvider) {
+ super(dataProvider);
+ }
+
+ @Override
+ boolean isFalseTouch() {
+ float angle = getAngle();
+
+ if (angle == Float.MAX_VALUE) { // Unknown angle
+ return false;
+ }
+
+ if (getInteractionType() == LEFT_AFFORDANCE
+ || getInteractionType() == RIGHT_AFFORDANCE) {
+ return false;
+ }
+
+ float minAngle = DIAGONAL - HORIZONTAL_ANGLE_RANGE;
+ float maxAngle = DIAGONAL + HORIZONTAL_ANGLE_RANGE;
+ if (isVertical()) {
+ minAngle = DIAGONAL - VERTICAL_ANGLE_RANGE;
+ maxAngle = DIAGONAL + VERTICAL_ANGLE_RANGE;
+ }
+
+ return angleBetween(angle, minAngle, maxAngle)
+ || angleBetween(angle, minAngle + NINETY_DEG, maxAngle + NINETY_DEG)
+ || angleBetween(angle, minAngle - NINETY_DEG, maxAngle - NINETY_DEG)
+ || angleBetween(angle, minAngle + ONE_HUNDRED_EIGHTY_DEG,
+ maxAngle + ONE_HUNDRED_EIGHTY_DEG);
+ }
+
+ private boolean angleBetween(float angle, float min, float max) {
+ // No need to normalize angle as it is guaranteed to be between 0 and 2*PI.
+ min = normalizeAngle(min);
+ max = normalizeAngle(max);
+
+ if (min > max) { // Can happen when angle is close to 0.
+ return angle >= min || angle <= max;
+ }
+
+ return angle >= min && angle <= max;
+ }
+
+ private float normalizeAngle(float angle) {
+ if (angle < 0) {
+ return THREE_HUNDRED_SIXTY_DEG + (angle % THREE_HUNDRED_SIXTY_DEG);
+ } else if (angle > THREE_HUNDRED_SIXTY_DEG) {
+ return angle % THREE_HUNDRED_SIXTY_DEG;
+ }
+ return angle;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
new file mode 100644
index 0000000..005ee12
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+
+import java.util.List;
+
+/**
+ * Ensure that the swipe + momentum covers a minimum distance.
+ */
+class DistanceClassifier extends FalsingClassifier {
+
+ private static final float HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN = 1;
+ private static final float VERTICAL_FLING_THRESHOLD_DISTANCE_IN = 1;
+ private static final float HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN = 3;
+ private static final float VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN = 3;
+ private static final float VELOCITY_TO_DISTANCE = 80f;
+ private static final float SCREEN_FRACTION_MIN_DISTANCE = 0.8f;
+
+ private final float mVerticalFlingThresholdPx;
+ private final float mHorizontalFlingThresholdPx;
+ private final float mVerticalSwipeThresholdPx;
+ private final float mHorizontalSwipeThresholdPx;
+
+ private boolean mDistanceDirty;
+ private DistanceVectors mCachedDistance;
+
+ DistanceClassifier(FalsingDataProvider dataProvider) {
+ super(dataProvider);
+
+ mHorizontalFlingThresholdPx = Math
+ .min(getWidthPixels() * SCREEN_FRACTION_MIN_DISTANCE,
+ HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN * getXdpi());
+ mVerticalFlingThresholdPx = Math
+ .min(getHeightPixels() * SCREEN_FRACTION_MIN_DISTANCE,
+ VERTICAL_FLING_THRESHOLD_DISTANCE_IN * getYdpi());
+ mHorizontalSwipeThresholdPx = Math
+ .min(getWidthPixels() * SCREEN_FRACTION_MIN_DISTANCE,
+ HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN * getXdpi());
+ mVerticalSwipeThresholdPx = Math
+ .min(getHeightPixels() * SCREEN_FRACTION_MIN_DISTANCE,
+ VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN * getYdpi());
+ mDistanceDirty = true;
+ }
+
+ private DistanceVectors getDistances() {
+ if (mDistanceDirty) {
+ mCachedDistance = calculateDistances();
+ mDistanceDirty = false;
+ }
+
+ return mCachedDistance;
+ }
+
+ private DistanceVectors calculateDistances() {
+ // This code assumes that there will be no missed DOWN or UP events.
+ VelocityTracker velocityTracker = VelocityTracker.obtain();
+ List<MotionEvent> motionEvents = getRecentMotionEvents();
+
+ if (motionEvents.size() < 3) {
+ logDebug("Only " + motionEvents.size() + " motion events recorded.");
+ return new DistanceVectors(0, 0, 0, 0);
+ }
+
+ for (MotionEvent motionEvent : motionEvents) {
+ velocityTracker.addMovement(motionEvent);
+ }
+ velocityTracker.computeCurrentVelocity(1);
+
+ float vX = velocityTracker.getXVelocity();
+ float vY = velocityTracker.getYVelocity();
+
+ velocityTracker.recycle();
+
+ float dX = getLastMotionEvent().getX() - getFirstMotionEvent().getX();
+ float dY = getLastMotionEvent().getY() - getFirstMotionEvent().getY();
+
+ logInfo("dX: " + dX + " dY: " + dY + " xV: " + vX + " yV: " + vY);
+
+ return new DistanceVectors(dX, dY, vX, vY);
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent motionEvent) {
+ mDistanceDirty = true;
+ }
+
+ @Override
+ public boolean isFalseTouch() {
+ return !getDistances().getPassedFlingThreshold();
+ }
+
+ boolean isLongSwipe() {
+ boolean longSwipe = getDistances().getPassedDistanceThreshold();
+ logDebug("Is longSwipe? " + longSwipe);
+ return longSwipe;
+ }
+
+ private class DistanceVectors {
+ final float mDx;
+ final float mDy;
+ private final float mVx;
+ private final float mVy;
+
+ DistanceVectors(float dX, float dY, float vX, float vY) {
+ this.mDx = dX;
+ this.mDy = dY;
+ this.mVx = vX;
+ this.mVy = vY;
+ }
+
+ boolean getPassedDistanceThreshold() {
+ if (isHorizontal()) {
+ logDebug("Horizontal swipe distance: " + Math.abs(mDx));
+ logDebug("Threshold: " + mHorizontalSwipeThresholdPx);
+
+ return Math.abs(mDx) >= mHorizontalSwipeThresholdPx;
+ }
+
+ logDebug("Vertical swipe distance: " + Math.abs(mDy));
+ logDebug("Threshold: " + mVerticalSwipeThresholdPx);
+ return Math.abs(mDy) >= mVerticalSwipeThresholdPx;
+ }
+
+ boolean getPassedFlingThreshold() {
+ float dX = this.mDx + this.mVx * VELOCITY_TO_DISTANCE;
+ float dY = this.mDy + this.mVy * VELOCITY_TO_DISTANCE;
+
+ if (isHorizontal()) {
+ logDebug("Horizontal swipe and fling distance: " + this.mDx + ", "
+ + this.mVx * VELOCITY_TO_DISTANCE);
+ logDebug("Threshold: " + mHorizontalFlingThresholdPx);
+ return Math.abs(dX) >= mHorizontalFlingThresholdPx;
+ }
+
+ logDebug("Vertical swipe and fling distance: " + this.mDy + ", "
+ + this.mVy * VELOCITY_TO_DISTANCE);
+ logDebug("Threshold: " + mVerticalFlingThresholdPx);
+ return Math.abs(dY) >= mVerticalFlingThresholdPx;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
new file mode 100644
index 0000000..685e7c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import android.hardware.SensorEvent;
+import android.view.MotionEvent;
+
+import com.android.systemui.classifier.Classifier;
+
+import java.util.List;
+
+/**
+ * Base class for rules that determine False touches.
+ */
+abstract class FalsingClassifier {
+ private final FalsingDataProvider mDataProvider;
+
+ FalsingClassifier(FalsingDataProvider dataProvider) {
+ this.mDataProvider = dataProvider;
+ }
+
+ List<MotionEvent> getRecentMotionEvents() {
+ return mDataProvider.getRecentMotionEvents();
+ }
+
+ MotionEvent getFirstMotionEvent() {
+ return mDataProvider.getFirstRecentMotionEvent();
+ }
+
+ MotionEvent getLastMotionEvent() {
+ return mDataProvider.getLastMotionEvent();
+ }
+
+ boolean isHorizontal() {
+ return mDataProvider.isHorizontal();
+ }
+
+ boolean isRight() {
+ return mDataProvider.isRight();
+ }
+
+ boolean isVertical() {
+ return mDataProvider.isVertical();
+ }
+
+ boolean isUp() {
+ return mDataProvider.isUp();
+ }
+
+ float getAngle() {
+ return mDataProvider.getAngle();
+ }
+
+ int getWidthPixels() {
+ return mDataProvider.getWidthPixels();
+ }
+
+ int getHeightPixels() {
+ return mDataProvider.getHeightPixels();
+ }
+
+ float getXdpi() {
+ return mDataProvider.getXdpi();
+ }
+
+ float getYdpi() {
+ return mDataProvider.getYdpi();
+ }
+
+ final @Classifier.InteractionType int getInteractionType() {
+ return mDataProvider.getInteractionType();
+ }
+
+ final void setInteractionType(@Classifier.InteractionType int interactionType) {
+ mDataProvider.setInteractionType(interactionType);
+ }
+
+ /**
+ * Called whenever a MotionEvent occurs.
+ *
+ * Useful for classifiers that need to see every MotionEvent, but most can probably
+ * use {@link #getRecentMotionEvents()} instead, which will return a list of MotionEvents.
+ */
+ void onTouchEvent(MotionEvent motionEvent) {};
+
+ /**
+ * Called whenever a SensorEvent occurs, specifically the ProximitySensor.
+ */
+ void onSensorEvent(SensorEvent sensorEvent) {};
+
+ /**
+ * The phone screen has turned on and we need to begin falsing detection.
+ */
+ void onSessionStarted() {};
+
+ /**
+ * The phone screen has turned off and falsing data can be discarded.
+ */
+ void onSessionEnded() {};
+
+ /**
+ * Returns true if the data captured so far looks like a false touch.
+ */
+ abstract boolean isFalseTouch();
+
+ static void logDebug(String msg) {
+ BrightLineFalsingManager.logDebug(msg);
+ }
+
+ static void logInfo(String msg) {
+ BrightLineFalsingManager.logInfo(msg);
+ }
+
+ static void logError(String msg) {
+ BrightLineFalsingManager.logError(msg);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
new file mode 100644
index 0000000..884c011
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+
+import com.android.systemui.classifier.Classifier;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Acts as a cache and utility class for FalsingClassifiers.
+ */
+class FalsingDataProvider {
+
+ private static final long MOTION_EVENT_AGE_MS = 1000;
+ private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI);
+
+ private final int mWidthPixels;
+ private final int mHeightPixels;
+ private final float mXdpi;
+ private final float mYdpi;
+
+ private @Classifier.InteractionType int mInteractionType;
+ private final TimeLimitedMotionEventBuffer mRecentMotionEvents =
+ new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS);
+
+ private boolean mDirty = true;
+
+ private float mAngle = 0;
+ private MotionEvent mFirstActualMotionEvent;
+ private MotionEvent mFirstRecentMotionEvent;
+ private MotionEvent mLastMotionEvent;
+
+ FalsingDataProvider(Context context) {
+ DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+ mXdpi = displayMetrics.xdpi;
+ mYdpi = displayMetrics.ydpi;
+ mWidthPixels = displayMetrics.widthPixels;
+ mHeightPixels = displayMetrics.heightPixels;
+
+ FalsingClassifier.logInfo("xdpi, ydpi: " + getXdpi() + ", " + getYdpi());
+ FalsingClassifier.logInfo("width, height: " + getWidthPixels() + ", " + getHeightPixels());
+ }
+
+ void onMotionEvent(MotionEvent motionEvent) {
+ if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mFirstActualMotionEvent = motionEvent;
+ }
+
+ List<MotionEvent> motionEvents = unpackMotionEvent(motionEvent);
+ FalsingClassifier.logDebug("Unpacked into: " + motionEvents.size());
+ if (BrightLineFalsingManager.DEBUG) {
+ for (MotionEvent m : motionEvents) {
+ FalsingClassifier.logDebug(
+ "x,y,t: " + m.getX() + "," + m.getY() + "," + m.getEventTime());
+ }
+ }
+
+ if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mRecentMotionEvents.clear();
+ }
+ mRecentMotionEvents.addAll(motionEvents);
+
+ FalsingClassifier.logDebug("Size: " + mRecentMotionEvents.size());
+
+ mDirty = true;
+ }
+
+ /** Returns screen width in pixels. */
+ int getWidthPixels() {
+ return mWidthPixels;
+ }
+
+ /** Returns screen height in pixels. */
+ int getHeightPixels() {
+ return mHeightPixels;
+ }
+
+ float getXdpi() {
+ return mXdpi;
+ }
+
+ float getYdpi() {
+ return mYdpi;
+ }
+
+ List<MotionEvent> getRecentMotionEvents() {
+ return mRecentMotionEvents;
+ }
+
+ /**
+ * interactionType is defined by {@link com.android.systemui.classifier.Classifier}.
+ */
+ final void setInteractionType(@Classifier.InteractionType int interactionType) {
+ this.mInteractionType = interactionType;
+ }
+
+ final int getInteractionType() {
+ return mInteractionType;
+ }
+
+ MotionEvent getFirstActualMotionEvent() {
+ return mFirstActualMotionEvent;
+ }
+
+ MotionEvent getFirstRecentMotionEvent() {
+ recalculateData();
+ return mFirstRecentMotionEvent;
+ }
+
+ MotionEvent getLastMotionEvent() {
+ recalculateData();
+ return mLastMotionEvent;
+ }
+
+ /**
+ * Returns the angle between the first and last point of the recent points.
+ *
+ * The angle will be in radians, always be between 0 and 2*PI, inclusive.
+ */
+ float getAngle() {
+ recalculateData();
+ return mAngle;
+ }
+
+ boolean isHorizontal() {
+ recalculateData();
+ return Math.abs(mFirstRecentMotionEvent.getX() - mLastMotionEvent.getX()) > Math
+ .abs(mFirstRecentMotionEvent.getY() - mLastMotionEvent.getY());
+ }
+
+ boolean isRight() {
+ recalculateData();
+ return mLastMotionEvent.getX() > mFirstRecentMotionEvent.getX();
+ }
+
+ boolean isVertical() {
+ return !isHorizontal();
+ }
+
+ boolean isUp() {
+ recalculateData();
+ return mLastMotionEvent.getY() < mFirstRecentMotionEvent.getY();
+ }
+
+ private void recalculateData() {
+ if (!mDirty) {
+ return;
+ }
+
+ mFirstRecentMotionEvent = mRecentMotionEvents.get(0);
+ mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1);
+
+ calculateAngleInternal();
+
+ mDirty = false;
+ }
+
+ private void calculateAngleInternal() {
+ if (mRecentMotionEvents.size() < 2) {
+ mAngle = Float.MAX_VALUE;
+ } else {
+ float lastX = mLastMotionEvent.getX() - mFirstRecentMotionEvent.getX();
+ float lastY = mLastMotionEvent.getY() - mFirstRecentMotionEvent.getY();
+
+ mAngle = (float) Math.atan2(lastY, lastX);
+ while (mAngle < 0) {
+ mAngle += THREE_HUNDRED_SIXTY_DEG;
+ }
+ while (mAngle > THREE_HUNDRED_SIXTY_DEG) {
+ mAngle -= THREE_HUNDRED_SIXTY_DEG;
+ }
+ }
+ }
+
+ private List<MotionEvent> unpackMotionEvent(MotionEvent motionEvent) {
+ List<MotionEvent> motionEvents = new ArrayList<>();
+ List<PointerProperties> pointerPropertiesList = new ArrayList<>();
+ int pointerCount = motionEvent.getPointerCount();
+ for (int i = 0; i < pointerCount; i++) {
+ PointerProperties pointerProperties = new PointerProperties();
+ motionEvent.getPointerProperties(i, pointerProperties);
+ pointerPropertiesList.add(pointerProperties);
+ }
+ PointerProperties[] pointerPropertiesArray = new PointerProperties[pointerPropertiesList
+ .size()];
+ pointerPropertiesList.toArray(pointerPropertiesArray);
+
+ int historySize = motionEvent.getHistorySize();
+ for (int i = 0; i < historySize; i++) {
+ List<PointerCoords> pointerCoordsList = new ArrayList<>();
+ for (int j = 0; j < pointerCount; j++) {
+ PointerCoords pointerCoords = new PointerCoords();
+ motionEvent.getHistoricalPointerCoords(j, i, pointerCoords);
+ pointerCoordsList.add(pointerCoords);
+ }
+ motionEvents.add(MotionEvent.obtain(
+ motionEvent.getDownTime(),
+ motionEvent.getHistoricalEventTime(i),
+ motionEvent.getAction(),
+ pointerCount,
+ pointerPropertiesArray,
+ pointerCoordsList.toArray(new PointerCoords[0]),
+ motionEvent.getMetaState(),
+ motionEvent.getButtonState(),
+ motionEvent.getXPrecision(),
+ motionEvent.getYPrecision(),
+ motionEvent.getDeviceId(),
+ motionEvent.getEdgeFlags(),
+ motionEvent.getSource(),
+ motionEvent.getFlags()
+ ));
+ }
+
+ motionEvents.add(MotionEvent.obtainNoHistory(motionEvent));
+
+ return motionEvents;
+ }
+
+ void onSessionEnd() {
+ mFirstActualMotionEvent = null;
+
+ for (MotionEvent ev : mRecentMotionEvents) {
+ ev.recycle();
+ }
+
+ mRecentMotionEvents.clear();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java
new file mode 100644
index 0000000..40e141f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import android.view.MotionEvent;
+
+/**
+ * False touch if more than one finger touches the screen.
+ *
+ * IMPORTANT: This should not be used for certain cases (i.e. a11y) as we expect multiple fingers
+ * for them.
+ */
+class PointerCountClassifier extends FalsingClassifier {
+
+ private static final int MAX_ALLOWED_POINTERS = 1;
+ private int mMaxPointerCount;
+
+ PointerCountClassifier(FalsingDataProvider dataProvider) {
+ super(dataProvider);
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent motionEvent) {
+ int pCount = mMaxPointerCount;
+ if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mMaxPointerCount = motionEvent.getPointerCount();
+ } else {
+ mMaxPointerCount = Math.max(mMaxPointerCount, motionEvent.getPointerCount());
+ }
+ if (pCount != mMaxPointerCount) {
+ logDebug("Pointers observed:" + mMaxPointerCount);
+ }
+ }
+
+ @Override
+ public boolean isFalseTouch() {
+ return mMaxPointerCount > MAX_ALLOWED_POINTERS;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
new file mode 100644
index 0000000..94a8ac85
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.view.MotionEvent;
+
+
+/**
+ * False touch if proximity sensor is covered for more than a certain percentage of the gesture.
+ *
+ * This classifer is essentially a no-op for QUICK_SETTINGS, as we assume the sensor may be
+ * covered when swiping from the top.
+ */
+class ProximityClassifier extends FalsingClassifier {
+
+ private static final double PERCENT_COVERED_THRESHOLD = 0.1;
+ private final DistanceClassifier mDistanceClassifier;
+
+ private boolean mNear;
+ private long mGestureStartTimeNs;
+ private long mPrevNearTimeNs;
+ private long mNearDurationNs;
+ private float mPercentNear;
+
+ ProximityClassifier(DistanceClassifier distanceClassifier,
+ FalsingDataProvider dataProvider) {
+ super(dataProvider);
+ this.mDistanceClassifier = distanceClassifier;
+ }
+
+ @Override
+ void onSessionStarted() {
+ mPrevNearTimeNs = 0;
+ mPercentNear = 0;
+ }
+
+ @Override
+ void onSessionEnded() {
+ mPrevNearTimeNs = 0;
+ mPercentNear = 0;
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent motionEvent) {
+ int action = motionEvent.getActionMasked();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mGestureStartTimeNs = motionEvent.getEventTimeNano();
+ if (mPrevNearTimeNs > 0) {
+ // We only care about if the proximity sensor is triggered while a move event is
+ // happening.
+ mPrevNearTimeNs = motionEvent.getEventTimeNano();
+ }
+ logDebug("Gesture start time: " + mGestureStartTimeNs);
+ mNearDurationNs = 0;
+ }
+
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ update(mNear, motionEvent.getEventTimeNano());
+ long duration = motionEvent.getEventTimeNano() - mGestureStartTimeNs;
+
+ logDebug("Gesture duration, Proximity duration: " + duration + ", " + mNearDurationNs);
+
+ if (duration == 0) {
+ mPercentNear = mNear ? 1.0f : 0.0f;
+ } else {
+ mPercentNear = (float) mNearDurationNs / (float) duration;
+ }
+ }
+
+ }
+
+ @Override
+ public void onSensorEvent(SensorEvent sensorEvent) {
+ if (sensorEvent.sensor.getType() == Sensor.TYPE_PROXIMITY) {
+ logDebug("Sensor is: " + (sensorEvent.values[0] < sensorEvent.sensor.getMaximumRange())
+ + " at time " + sensorEvent.timestamp);
+ update(
+ sensorEvent.values[0] < sensorEvent.sensor.getMaximumRange(),
+ sensorEvent.timestamp);
+ }
+ }
+
+ @Override
+ public boolean isFalseTouch() {
+ if (getInteractionType() == QUICK_SETTINGS) {
+ return false;
+ }
+
+ logInfo("Percent of gesture in proximity: " + mPercentNear);
+
+ if (mPercentNear > PERCENT_COVERED_THRESHOLD) {
+ return !mDistanceClassifier.isLongSwipe();
+ }
+
+ return false;
+ }
+
+ /**
+ * @param near is the sensor showing the near state right now
+ * @param timeStampNs time of this event in nanoseconds
+ */
+ private void update(boolean near, long timeStampNs) {
+ if (mPrevNearTimeNs != 0 && timeStampNs > mPrevNearTimeNs && mNear) {
+ mNearDurationNs += timeStampNs - mPrevNearTimeNs;
+ logDebug("Updating duration: " + mNearDurationNs);
+ }
+
+ if (near) {
+ logDebug("Set prevNearTimeNs: " + timeStampNs);
+ mPrevNearTimeNs = timeStampNs;
+ }
+
+ mNear = near;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java
new file mode 100644
index 0000000..9a83b5b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java
@@ -0,0 +1,242 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import android.view.MotionEvent;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * Maintains an ordered list of the last N milliseconds of MotionEvents.
+ *
+ * This class is simply a convenience class designed to look like a simple list, but that
+ * automatically discards old MotionEvents. It functions much like a queue - first in first out -
+ * but does not have a fixed size like a circular buffer.
+ */
+public class TimeLimitedMotionEventBuffer implements List<MotionEvent> {
+
+ private final LinkedList<MotionEvent> mMotionEvents;
+ private long mMaxAgeMs;
+
+ TimeLimitedMotionEventBuffer(long maxAgeMs) {
+ super();
+ this.mMaxAgeMs = maxAgeMs;
+ this.mMotionEvents = new LinkedList<>();
+ }
+
+ private void ejectOldEvents() {
+ if (mMotionEvents.isEmpty()) {
+ return;
+ }
+ Iterator<MotionEvent> iter = listIterator();
+ long mostRecentMs = mMotionEvents.getLast().getEventTime();
+ while (iter.hasNext()) {
+ MotionEvent ev = iter.next();
+ if (mostRecentMs - ev.getEventTime() > mMaxAgeMs) {
+ iter.remove();
+ ev.recycle();
+ }
+ }
+ }
+
+ @Override
+ public void add(int index, MotionEvent element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public MotionEvent remove(int index) {
+ return mMotionEvents.remove(index);
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ return mMotionEvents.indexOf(o);
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ return mMotionEvents.lastIndexOf(o);
+ }
+
+ @Override
+ public int size() {
+ return mMotionEvents.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return mMotionEvents.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return mMotionEvents.contains(o);
+ }
+
+ @Override
+ public Iterator<MotionEvent> iterator() {
+ return mMotionEvents.iterator();
+ }
+
+ @Override
+ public Object[] toArray() {
+ return mMotionEvents.toArray();
+ }
+
+ @Override
+ public <T> T[] toArray(T[] a) {
+ return mMotionEvents.toArray(a);
+ }
+
+ @Override
+ public boolean add(MotionEvent element) {
+ boolean result = mMotionEvents.add(element);
+ ejectOldEvents();
+ return result;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return mMotionEvents.remove(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return mMotionEvents.containsAll(c);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends MotionEvent> collection) {
+ boolean result = mMotionEvents.addAll(collection);
+ ejectOldEvents();
+ return result;
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends MotionEvent> elements) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ return mMotionEvents.removeAll(c);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ return mMotionEvents.retainAll(c);
+ }
+
+ @Override
+ public void clear() {
+ mMotionEvents.clear();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return mMotionEvents.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return mMotionEvents.hashCode();
+ }
+
+ @Override
+ public MotionEvent get(int index) {
+ return mMotionEvents.get(index);
+ }
+
+ @Override
+ public MotionEvent set(int index, MotionEvent element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListIterator<MotionEvent> listIterator() {
+ return new Iter(0);
+ }
+
+ @Override
+ public ListIterator<MotionEvent> listIterator(int index) {
+ return new Iter(index);
+ }
+
+ @Override
+ public List<MotionEvent> subList(int fromIndex, int toIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ class Iter implements ListIterator<MotionEvent> {
+
+ private final ListIterator<MotionEvent> mIterator;
+
+ Iter(int index) {
+ this.mIterator = mMotionEvents.listIterator(index);
+ }
+
+ @Override
+ public boolean hasNext() {
+ return mIterator.hasNext();
+ }
+
+ @Override
+ public MotionEvent next() {
+ return mIterator.next();
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return mIterator.hasPrevious();
+ }
+
+ @Override
+ public MotionEvent previous() {
+ return mIterator.previous();
+ }
+
+ @Override
+ public int nextIndex() {
+ return mIterator.nextIndex();
+ }
+
+ @Override
+ public int previousIndex() {
+ return mIterator.previousIndex();
+ }
+
+ @Override
+ public void remove() {
+ mIterator.remove();
+ }
+
+ @Override
+ public void set(MotionEvent motionEvent) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void add(MotionEvent element) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java
new file mode 100644
index 0000000..b6ceab5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+
+import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
+import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
+import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
+import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
+import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
+import static com.android.systemui.classifier.Classifier.UNLOCK;
+
+/**
+ * Ensure that the swipe direction generally matches that of the interaction type.
+ */
+public class TypeClassifier extends FalsingClassifier {
+ TypeClassifier(FalsingDataProvider dataProvider) {
+ super(dataProvider);
+ }
+
+ @Override
+ public boolean isFalseTouch() {
+ boolean vertical = isVertical();
+ boolean up = isUp();
+ boolean right = isRight();
+
+ switch (getInteractionType()) {
+ case QUICK_SETTINGS:
+ case PULSE_EXPAND:
+ case NOTIFICATION_DRAG_DOWN:
+ return !vertical || up;
+ case NOTIFICATION_DISMISS:
+ return vertical;
+ case UNLOCK:
+ case BOUNCER_UNLOCK:
+ return !vertical || !up;
+ case LEFT_AFFORDANCE: // Swiping from the bottom left corner for camera or similar.
+ return !right || !up;
+ case RIGHT_AFFORDANCE: // Swiping from the bottom right corner for camera or similar.
+ return right || !up;
+ default:
+ return true;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
new file mode 100644
index 0000000..a62574f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import android.graphics.Point;
+import android.view.MotionEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Penalizes gestures that change direction in either the x or y too much.
+ */
+class ZigZagClassifier extends FalsingClassifier {
+
+ // Define how far one can move back and forth over one inch of travel before being falsed.
+ // `PRIMARY` defines how far one can deviate in the primary direction of travel. I.e. if you're
+ // swiping vertically, you shouldn't have a lot of zig zag in the vertical direction. Since
+ // most swipes will follow somewhat of a 'C' or 'S' shape, we allow more deviance along the
+ // `SECONDARY` axis.
+ private static final float MAX_X_PRIMARY_DEVIANCE = .05f;
+ private static final float MAX_Y_PRIMARY_DEVIANCE = .05f;
+ private static final float MAX_X_SECONDARY_DEVIANCE = .3f;
+ private static final float MAX_Y_SECONDARY_DEVIANCE = .3f;
+
+ ZigZagClassifier(FalsingDataProvider dataProvider) {
+ super(dataProvider);
+ }
+
+ @Override
+ boolean isFalseTouch() {
+ List<MotionEvent> motionEvents = getRecentMotionEvents();
+ // Rotate horizontal gestures to be horizontal between their first and last point.
+ // Rotate vertical gestures to be vertical between their first and last point.
+ // Sum the absolute value of every dx and dy along the gesture. Compare this with the dx
+ // and dy
+ // between the first and last point.
+ // For horizontal lines, the difference in the x direction should be small.
+ // For vertical lines, the difference in the y direction should be small.
+
+ if (motionEvents.size() < 3) {
+ return false;
+ }
+
+ List<Point> rotatedPoints;
+ if (isHorizontal()) {
+ rotatedPoints = rotateHorizontal();
+ } else {
+ rotatedPoints = rotateVertical();
+ }
+
+ float actualDx = Math
+ .abs(rotatedPoints.get(0).x - rotatedPoints.get(rotatedPoints.size() - 1).x);
+ float actualDy = Math
+ .abs(rotatedPoints.get(0).y - rotatedPoints.get(rotatedPoints.size() - 1).y);
+ logDebug("Actual: (" + actualDx + "," + actualDy + ")");
+ float runningAbsDx = 0;
+ float runningAbsDy = 0;
+ float pX = 0;
+ float pY = 0;
+ boolean firstLoop = true;
+ for (Point point : rotatedPoints) {
+ if (firstLoop) {
+ pX = point.x;
+ pY = point.y;
+ firstLoop = false;
+ continue;
+ }
+ runningAbsDx += Math.abs(point.x - pX);
+ runningAbsDy += Math.abs(point.y - pY);
+ pX = point.x;
+ pY = point.y;
+ logDebug("(x, y, runningAbsDx, runningAbsDy) - (" + pX + ", " + pY + ", " + runningAbsDx
+ + ", " + runningAbsDy + ")");
+ }
+
+ float devianceX = runningAbsDx - actualDx;
+ float devianceY = runningAbsDy - actualDy;
+ float distanceXIn = actualDx / getXdpi();
+ float distanceYIn = actualDy / getYdpi();
+ float totalDistanceIn = (float) Math
+ .sqrt(distanceXIn * distanceXIn + distanceYIn * distanceYIn);
+
+ float maxXDeviance;
+ float maxYDeviance;
+ if (actualDx > actualDy) {
+ maxXDeviance = MAX_X_PRIMARY_DEVIANCE * totalDistanceIn * getXdpi();
+ maxYDeviance = MAX_Y_SECONDARY_DEVIANCE * totalDistanceIn * getYdpi();
+ } else {
+ maxXDeviance = MAX_X_SECONDARY_DEVIANCE * totalDistanceIn * getXdpi();
+ maxYDeviance = MAX_Y_PRIMARY_DEVIANCE * totalDistanceIn * getYdpi();
+ }
+
+ logDebug("Straightness Deviance: (" + devianceX + "," + devianceY + ") vs "
+ + "(" + maxXDeviance + "," + maxYDeviance + ")");
+ return devianceX > maxXDeviance || devianceY > maxYDeviance;
+ }
+
+ private float getAtan2LastPoint() {
+ MotionEvent firstEvent = getFirstMotionEvent();
+ MotionEvent lastEvent = getLastMotionEvent();
+ float offsetX = firstEvent.getX();
+ float offsetY = firstEvent.getY();
+ float lastX = lastEvent.getX() - offsetX;
+ float lastY = lastEvent.getY() - offsetY;
+
+ return (float) Math.atan2(lastY, lastX);
+ }
+
+ private List<Point> rotateVertical() {
+ // Calculate the angle relative to the y axis.
+ double angle = Math.PI / 2 - getAtan2LastPoint();
+ logDebug("Rotating to vertical by: " + angle);
+ return rotateMotionEvents(getRecentMotionEvents(), -angle);
+ }
+
+ private List<Point> rotateHorizontal() {
+ // Calculate the angle relative to the x axis.
+ double angle = getAtan2LastPoint();
+ logDebug("Rotating to horizontal by: " + angle);
+ return rotateMotionEvents(getRecentMotionEvents(), angle);
+ }
+
+ private List<Point> rotateMotionEvents(List<MotionEvent> motionEvents, double angle) {
+ List<Point> points = new ArrayList<>();
+ double cosAngle = Math.cos(angle);
+ double sinAngle = Math.sin(angle);
+ MotionEvent firstEvent = motionEvents.get(0);
+ float offsetX = firstEvent.getX();
+ float offsetY = firstEvent.getY();
+ for (MotionEvent motionEvent : motionEvents) {
+ float x = motionEvent.getX() - offsetX;
+ float y = motionEvent.getY() - offsetY;
+ double rotatedX = cosAngle * x + sinAngle * y + offsetX;
+ double rotatedY = -sinAngle * x + cosAngle * y + offsetY;
+ points.add(new Point((int) rotatedX, (int) rotatedY));
+ }
+
+ MotionEvent lastEvent = motionEvents.get(motionEvents.size() - 1);
+ Point firstPoint = points.get(0);
+ Point lastPoint = points.get(points.size() - 1);
+ logDebug(
+ "Before: (" + firstEvent.getX() + "," + firstEvent.getY() + "), ("
+ + lastEvent.getX() + ","
+ + lastEvent.getY() + ")");
+ logDebug(
+ "After: (" + firstPoint.x + "," + firstPoint.y + "), (" + lastPoint.x + ","
+ + lastPoint.y
+ + ")");
+
+ return points;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 88a8b31..e5caf68 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -27,6 +27,9 @@
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import android.annotation.FloatRange;
import android.app.ActivityTaskManager;
@@ -67,6 +70,7 @@
import com.android.systemui.statusbar.phone.NavigationBarView;
import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -90,7 +94,6 @@
private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
public static final String TAG_OPS = "OverviewProxyService";
- public static final boolean DEBUG_OVERVIEW_PROXY = false;
private static final long BACKOFF_MILLIS = 1000;
private static final long DEFERRED_CALLBACK_MILLIS = 5000;
@@ -115,7 +118,7 @@
private boolean mBound;
private boolean mIsEnabled;
private int mCurrentBoundedUserId = -1;
- private float mBackButtonAlpha;
+ private float mNavBarButtonAlpha;
private MotionEvent mStatusBarGestureDownEvent;
private float mWindowCornerRadius;
private boolean mSupportsRoundedCornersOnWindows;
@@ -241,22 +244,25 @@
}
@Override
- public void setBackButtonAlpha(float alpha, boolean animate) {
- if (!verifyCaller("setBackButtonAlpha")) {
+ public void setNavBarButtonAlpha(float alpha, boolean animate) {
+ if (!verifyCaller("setNavBarButtonAlpha")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
- mBackButtonAlpha = alpha;
- mHandler.post(() -> {
- notifyBackButtonAlphaChanged(alpha, animate);
- });
+ mNavBarButtonAlpha = alpha;
+ mHandler.post(() -> notifyNavBarButtonAlphaChanged(alpha, animate));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
+ public void setBackButtonAlpha(float alpha, boolean animate) {
+ setNavBarButtonAlpha(alpha, animate);
+ }
+
+ @Override
public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
if (!verifyCaller("onAssistantProgress")) {
return;
@@ -442,6 +448,8 @@
}
};
+ private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
+
// This is the death handler for the binder from the launcher service
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
= this::cleanupAfterDeath;
@@ -465,7 +473,7 @@
.supportsRoundedCornersOnWindows(mContext.getResources());
// Assumes device always starts with back button until launcher tells it that it does not
- mBackButtonAlpha = 1.0f;
+ mNavBarButtonAlpha = 1.0f;
// Listen for nav bar mode changes
mNavBarMode = navModeController.addListener(this);
@@ -481,6 +489,9 @@
PatternMatcher.PATTERN_LITERAL);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
+
+ // Listen for status bar state changes
+ statusBarWinController.registerCallback(mStatusBarWindowCallback);
}
public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton,
@@ -531,7 +542,7 @@
navBarView.updateSystemUiStateFlags();
}
if (mStatusBarWinController != null) {
- mStatusBarWinController.updateSystemUiStateFlags();
+ mStatusBarWinController.notifyStateChangedCallbacks();
}
notifySystemUiStateFlags(mSysUiStateFlags);
}
@@ -546,6 +557,16 @@
}
}
+ private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
+ boolean bouncerShowing) {
+ int displayId = mContext.getDisplayId();
+ setSystemUiStateFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
+ keyguardShowing && !keyguardOccluded, displayId);
+ setSystemUiStateFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
+ keyguardShowing && keyguardOccluded, displayId);
+ setSystemUiStateFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing, displayId);
+ }
+
/**
* Sets the navbar region which can receive touch inputs
*/
@@ -565,7 +586,7 @@
}
public float getBackButtonAlpha() {
- return mBackButtonAlpha;
+ return mNavBarButtonAlpha;
}
public void cleanupAfterDeath() {
@@ -637,7 +658,7 @@
public void addCallback(OverviewProxyListener listener) {
mConnectionCallbacks.add(listener);
listener.onConnectionChanged(mOverviewProxy != null);
- listener.onBackButtonAlphaChanged(mBackButtonAlpha, false);
+ listener.onNavBarButtonAlphaChanged(mNavBarButtonAlpha, false);
listener.onSystemUiStateChanged(mSysUiStateFlags);
}
@@ -668,14 +689,14 @@
if (mOverviewProxy != null) {
mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
mOverviewProxy = null;
- notifyBackButtonAlphaChanged(1f, false /* animate */);
+ notifyNavBarButtonAlphaChanged(1f, false /* animate */);
notifyConnectionChanged();
}
}
- private void notifyBackButtonAlphaChanged(float alpha, boolean animate) {
+ private void notifyNavBarButtonAlphaChanged(float alpha, boolean animate) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
- mConnectionCallbacks.get(i).onBackButtonAlphaChanged(alpha, animate);
+ mConnectionCallbacks.get(i).onNavBarButtonAlphaChanged(alpha, animate);
}
}
@@ -766,7 +787,8 @@
default void onQuickStepStarted() {}
default void onOverviewShown(boolean fromHome) {}
default void onQuickScrubStarted() {}
- default void onBackButtonAlphaChanged(float alpha, boolean animate) {}
+ /** Notify changes in the nav bar button alpha */
+ default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {}
default void onSystemUiStateChanged(int sysuiStateFlags) {}
default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
default void onAssistantGestureCompletion(float velocity) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index 5747bb1..170a4d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -19,7 +19,6 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import android.annotation.Nullable;
-import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
import android.view.LayoutInflater;
@@ -32,6 +31,8 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
/**
* Manages the boundaries of the two notification sections (high priority and low priority). Also
@@ -43,8 +44,10 @@
private final NotificationStackScrollLayout mParent;
private final ActivityStarter mActivityStarter;
private final StatusBarStateController mStatusBarStateController;
+ private final ConfigurationController mConfigurationController;
private final boolean mUseMultipleSections;
+ private boolean mInitialized = false;
private SectionHeaderView mGentleHeader;
private boolean mGentleHeaderVisible = false;
@Nullable private ExpandableNotificationRow mFirstGentleNotif;
@@ -54,18 +57,29 @@
NotificationStackScrollLayout parent,
ActivityStarter activityStarter,
StatusBarStateController statusBarStateController,
+ ConfigurationController configurationController,
boolean useMultipleSections) {
mParent = parent;
mActivityStarter = activityStarter;
mStatusBarStateController = statusBarStateController;
+ mConfigurationController = configurationController;
mUseMultipleSections = useMultipleSections;
}
+ /** Must be called before use. */
+ void initialize(LayoutInflater layoutInflater) {
+ if (mInitialized) {
+ throw new IllegalStateException("NotificationSectionsManager already initialized");
+ }
+ mInitialized = true;
+ reinflateViews(layoutInflater);
+ mConfigurationController.addCallback(mConfigurationListener);
+ }
+
/**
- * Must be called before use. Should be called again whenever inflation-related things change,
- * such as density or theme changes.
+ * Reinflates the entire notification header, including all decoration views.
*/
- void inflateViews(Context context) {
+ void reinflateViews(LayoutInflater layoutInflater) {
int oldPos = -1;
if (mGentleHeader != null) {
if (mGentleHeader.getTransientContainer() != null) {
@@ -76,7 +90,7 @@
}
}
- mGentleHeader = (SectionHeaderView) LayoutInflater.from(context).inflate(
+ mGentleHeader = (SectionHeaderView) layoutInflater.inflate(
R.layout.status_bar_notification_section_header, mParent, false);
mGentleHeader.setOnHeaderClickListener(this::onGentleHeaderClick);
mGentleHeader.setOnClearAllClickListener(this::onClearGentleNotifsClick);
@@ -244,6 +258,13 @@
return lastChildBeforeGap;
}
+ private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+ @Override
+ public void onLocaleListChanged() {
+ mGentleHeader.reinflateContents();
+ }
+ };
+
private void onGentleHeaderClick(View v) {
Intent intent = new Intent(Settings.ACTION_NOTIFICATION_SETTINGS);
mActivityStarter.startActivity(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index c214431..6e9fe67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -32,7 +32,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@@ -515,6 +514,7 @@
NotificationRoundnessManager notificationRoundnessManager,
AmbientPulseManager ambientPulseManager,
DynamicPrivacyController dynamicPrivacyController,
+ ConfigurationController configurationController,
ActivityStarter activityStarter,
StatusBarStateController statusBarStateController) {
super(context, attrs, 0, 0);
@@ -533,8 +533,9 @@
this,
activityStarter,
statusBarStateController,
+ configurationController,
NotificationUtils.useNewInterruptionModel(context));
- mSectionsManager.inflateViews(context);
+ mSectionsManager.initialize(LayoutInflater.from(context));
mSectionsManager.setOnClearGentleNotifsClickListener(v -> {
// Leave the shade open if there will be other notifs left over to clear
final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
@@ -648,7 +649,7 @@
inflateFooterView();
inflateEmptyShadeView();
updateFooter();
- mSectionsManager.inflateViews(mContext);
+ mSectionsManager.reinflateViews(LayoutInflater.from(mContext));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index e2f702d..cc1170f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -16,11 +16,16 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.RectF;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
@@ -32,9 +37,10 @@
* notification sections. Currently only used for gentle notifications.
*/
public class SectionHeaderView extends ActivatableNotificationView {
- private View mContents;
+ private ViewGroup mContents;
private TextView mLabelView;
private ImageView mClearAllButton;
+ @Nullable private View.OnClickListener mOnClearClickListener = null;
private final RectF mTmpRect = new RectF();
@@ -45,9 +51,16 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mContents = findViewById(R.id.content);
- mLabelView = findViewById(R.id.header_label);
- mClearAllButton = findViewById(R.id.btn_clear_all);
+ mContents = checkNotNull(findViewById(R.id.content));
+ bindContents();
+ }
+
+ private void bindContents() {
+ mLabelView = checkNotNull(findViewById(R.id.header_label));
+ mClearAllButton = checkNotNull(findViewById(R.id.btn_clear_all));
+ if (mOnClearClickListener != null) {
+ mClearAllButton.setOnClickListener(mOnClearClickListener);
+ }
}
@Override
@@ -55,6 +68,21 @@
return mContents;
}
+ /**
+ * Destroys and reinflates the visible contents of the section header. For use on configuration
+ * changes or any other time that layout values might need to be re-evaluated.
+ *
+ * Does not reinflate the base content view itself ({@link #getContentView()} or any of the
+ * decorator views, such as the background view or shadow view.
+ */
+ void reinflateContents() {
+ mContents.removeAllViews();
+ LayoutInflater.from(getContext()).inflate(
+ R.layout.status_bar_notification_section_header_contents,
+ mContents);
+ bindContents();
+ }
+
/** Must be called whenever the UI mode changes (i.e. when we enter night mode). */
void onUiModeChanged() {
updateBackgroundColors();
@@ -88,6 +116,7 @@
/** Fired when the user clicks on the "X" button on the far right of the header. */
void setOnClearAllClickListener(View.OnClickListener listener) {
+ mOnClearClickListener = listener;
mClearAllButton.setOnClickListener(listener);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 38ff468..cc0bc5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -57,6 +57,7 @@
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.io.PrintWriter;
import java.util.concurrent.Executor;
/**
@@ -118,7 +119,7 @@
private final Region mExcludeRegion = new Region();
// The edge width where touch down is allowed
- private final int mEdgeWidth;
+ private int mEdgeWidth;
// The slop to distinguish between horizontal and vertical motion
private final float mTouchSlop;
// Duration after which we consider the event as longpress.
@@ -163,10 +164,6 @@
mWm = context.getSystemService(WindowManager.class);
mOverviewProxyService = overviewProxyService;
- // TODO: Get this for the current user
- mEdgeWidth = res.getDimensionPixelSize(
- com.android.internal.R.dimen.config_backGestureInset);
-
// Reduce the default touch slop to ensure that we can intercept the gesture
// before the app starts to react to it.
// TODO(b/130352502) Tune this value and extract into a constant
@@ -176,6 +173,12 @@
mNavBarHeight = res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height);
mMinArrowPosition = res.getDimensionPixelSize(R.dimen.navigation_edge_arrow_min_y);
mFingerOffset = res.getDimensionPixelSize(R.dimen.navigation_edge_finger_offset);
+ updateCurrentUserResources(res);
+ }
+
+ public void updateCurrentUserResources(Resources res) {
+ mEdgeWidth = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.config_backGestureInset);
}
/**
@@ -194,9 +197,10 @@
updateIsEnabled();
}
- public void onNavigationModeChanged(int mode) {
+ public void onNavigationModeChanged(int mode, Context currentUserContext) {
mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode);
updateIsEnabled();
+ updateCurrentUserResources(currentUserContext.getResources());
}
private void disposeInputChannel() {
@@ -270,6 +274,8 @@
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
+ mEdgePanelLp.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
mEdgePanelLp.setTitle(TAG + mDisplayId);
mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
mEdgePanelLp.windowAnimations = 0;
@@ -449,6 +455,16 @@
mRightInset = rightInset;
}
+ public void dump(PrintWriter pw) {
+ pw.println("EdgeBackGestureHandler:");
+ pw.println(" mIsEnabled=" + mIsEnabled);
+ pw.println(" mAllowGesture=" + mAllowGesture);
+ pw.println(" mExcludeRegion=" + mExcludeRegion);
+ pw.println(" mImeHeight=" + mImeHeight);
+ pw.println(" mIsAttached=" + mIsAttached);
+ pw.println(" mEdgeWidth=" + mEdgeWidth);
+ }
+
class SysUiInputEventReceiver extends InputEventReceiver {
SysUiInputEventReceiver(InputChannel channel, Looper looper) {
super(channel, looper);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
index 6bbeffa..deb314b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
@@ -115,7 +115,6 @@
return false;
}
mWindowManager.removeViewImmediate(mKeyButtonView);
- mRotationButtonController.cleanUp();
mIsShowing = false;
return true;
}
@@ -141,10 +140,7 @@
@Override
public void setOnClickListener(View.OnClickListener onClickListener) {
- mKeyButtonView.setOnClickListener(view -> {
- hide();
- onClickListener.onClick(view);
- });
+ mKeyButtonView.setOnClickListener(onClickListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
index 2f245ff..bfd17b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
@@ -45,6 +45,7 @@
private final NavigationBarView mNavigationBarView;
private final LightBarTransitionsController mLightBarController;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
+ private boolean mWindowVisible;
private final CompositionSamplingListener mSamplingListener;
private final Runnable mUpdateSamplingListener = this::updateSamplingListener;
@@ -148,7 +149,7 @@
mSamplingListenerRegistered = false;
CompositionSamplingListener.unregister(mSamplingListener);
}
- if (mSamplingEnabled && !mSamplingBounds.isEmpty()
+ if (mSamplingEnabled && mWindowVisible && !mSamplingBounds.isEmpty()
&& mNavigationBarView.isAttachedToWindow()) {
if (!mNavigationBarView.getViewRootImpl().getSurfaceControl().isValid()) {
// The view may still be attached, but the surface backing the window can be
@@ -180,6 +181,11 @@
}
}
+ public void setWindowVisible(boolean visible) {
+ mWindowVisible = visible;
+ requestUpdateSamplingListener();
+ }
+
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
}
@@ -194,6 +200,7 @@
pw.println(" mSamplingBounds: " + mSamplingBounds);
pw.println(" mLastMedianLuma: " + mLastMedianLuma);
pw.println(" mCurrentMedianLuma: " + mCurrentMedianLuma);
+ pw.println(" mWindowVisible: " + mWindowVisible);
}
public static boolean isEnabled(Context context, int navBarMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 79976d0..e9731c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -203,17 +203,16 @@
}
@Override
- public void onBackButtonAlphaChanged(float alpha, boolean animate) {
- final ButtonDispatcher backButton = mNavigationBarView.getBackButton();
- final boolean useAltBack =
- (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
- if (QuickStepContract.isGesturalMode(mNavBarMode) && !useAltBack) {
- // If property was changed to hide/show back button, going home will trigger
- // launcher to to change the back button alpha to reflect property change
- backButton.setVisibility(View.GONE);
- } else {
- backButton.setVisibility(alpha > 0 ? View.VISIBLE : View.INVISIBLE);
- backButton.setAlpha(alpha, animate);
+ public void onNavBarButtonAlphaChanged(float alpha, boolean animate) {
+ ButtonDispatcher buttonDispatcher = null;
+ if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
+ buttonDispatcher = mNavigationBarView.getBackButton();
+ } else if (QuickStepContract.isGesturalMode(mNavBarMode)) {
+ buttonDispatcher = mNavigationBarView.getHomeHandle();
+ }
+ if (buttonDispatcher != null) {
+ buttonDispatcher.setVisibility(alpha > 0 ? View.VISIBLE : View.INVISIBLE);
+ buttonDispatcher.setAlpha(alpha, animate);
}
}
};
@@ -323,6 +322,7 @@
mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
}
mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
+ mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
prepareNavigationBarView();
checkNavBarModes();
@@ -468,8 +468,7 @@
if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
updateSystemUiStateFlags(-1);
- mNavigationBarView.getRotationButtonController().onNavigationBarWindowVisibilityChange(
- isNavBarWindowVisible());
+ mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
}
}
@@ -872,9 +871,6 @@
boolean[] feedbackEnabled = new boolean[1];
int a11yFlags = getA11yButtonState(feedbackEnabled);
- mNavigationBarView.getRotationButtonController().setAccessibilityFeedbackEnabled(
- feedbackEnabled[0]);
-
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
mNavigationBarView.setAccessibilityButtonState(clickable, longClickable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 90bce39..776cd4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -533,6 +533,11 @@
return KeyButtonDrawable.create(mContext, icon, hasShadow);
}
+ public void setWindowVisible(boolean visible) {
+ mTintController.setWindowVisible(visible);
+ mRotationButtonController.onNavigationBarWindowVisibilityChange(visible);
+ }
+
@Override
public void setLayoutDirection(int layoutDirection) {
reloadNavIcons();
@@ -772,9 +777,10 @@
@Override
public void onNavigationModeChanged(int mode) {
+ Context curUserCtx = Dependency.get(NavigationModeController.class).getCurrentUserContext();
mNavBarMode = mode;
mBarTransitions.onNavigationModeChanged(mNavBarMode);
- mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
+ mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode, curUserCtx);
mRecentsOnboarding.onNavigationModeChanged(mNavBarMode);
getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode);
@@ -1039,6 +1045,9 @@
reorient();
onNavigationModeChanged(mNavBarMode);
setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
+ if (mRotationButtonController != null) {
+ mRotationButtonController.registerListeners();
+ }
mEdgeBackGestureHandler.onNavBarAttached();
getViewTreeObserver().addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
@@ -1052,6 +1061,10 @@
for (int i = 0; i < mButtonDispatchers.size(); ++i) {
mButtonDispatchers.valueAt(i).onDestroy();
}
+ if (mRotationButtonController != null) {
+ mRotationButtonController.unregisterListeners();
+ }
+
mEdgeBackGestureHandler.onNavBarDetached();
getViewTreeObserver().removeOnComputeInternalInsetsListener(
mOnComputeInternalInsetsListener);
@@ -1103,6 +1116,7 @@
mContextualButtonGroup.dump(pw);
mRecentsOnboarding.dump(pw);
mTintController.dump(pw);
+ mEdgeBackGestureHandler.dump(pw);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index 77eb469..1124220 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -224,7 +224,7 @@
return mode;
}
- private Context getCurrentUserContext() {
+ public Context getCurrentUserContext() {
int userId = ActivityManagerWrapper.getInstance().getCurrentUserId();
if (DEBUG) {
Log.d(TAG, "getCurrentUserContext: contextUser=" + mContext.getUserId()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
index 1e5406f..0147e7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
@@ -26,7 +26,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.os.Handler;
-import android.os.Message;
+import android.os.Looper;
import android.os.RemoteException;
import android.provider.Settings;
import android.view.IRotationWatcher.Stub;
@@ -34,6 +34,7 @@
import android.view.Surface;
import android.view.View;
import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -42,6 +43,7 @@
import com.android.systemui.R;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
import com.android.systemui.statusbar.policy.RotationLockController;
@@ -64,8 +66,10 @@
private boolean mPendingRotationSuggestion;
private boolean mHoveringRotationSuggestion;
private RotationLockController mRotationLockController;
+ private AccessibilityManagerWrapper mAccessibilityManagerWrapper;
private TaskStackListenerImpl mTaskStackListener;
private Consumer<Integer> mRotWatcherListener;
+ private boolean mListenersRegistered = false;
private boolean mIsNavigationBarShowing;
private final Runnable mRemoveRotationProposal =
@@ -73,22 +77,17 @@
private final Runnable mCancelPendingRotationProposal =
() -> mPendingRotationSuggestion = false;
private Animator mRotateHideAnimator;
- private boolean mAccessibilityFeedbackEnabled;
private final Context mContext;
private final RotationButton mRotationButton;
+ private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final Stub mRotationWatcher = new Stub() {
@Override
public void onRotationChanged(final int rotation) throws RemoteException {
- if (mRotationButton.getCurrentView() == null) {
- return;
- }
-
// We need this to be scheduled as early as possible to beat the redrawing of
// window in response to the orientation change.
- Handler h = mRotationButton.getCurrentView().getHandler();
- Message msg = Message.obtain(h, () -> {
+ mMainThreadHandler.postAtFrontOfQueue(() -> {
// If the screen rotation changes while locked, potentially update lock to flow with
// new screen rotation and hide any showing suggestions.
if (mRotationLockController.isRotationLocked()) {
@@ -102,8 +101,6 @@
mRotWatcherListener.accept(rotation);
}
});
- msg.setAsynchronous(true);
- h.sendMessageAtFrontOfQueue(msg);
}
};
@@ -124,40 +121,49 @@
mStyleRes = style;
mIsNavigationBarShowing = true;
mRotationLockController = Dependency.get(RotationLockController.class);
+ mAccessibilityManagerWrapper = Dependency.get(AccessibilityManagerWrapper.class);
// Register the task stack listener
mTaskStackListener = new TaskStackListenerImpl();
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
mRotationButton.setOnClickListener(this::onRotateSuggestionClick);
mRotationButton.setOnHoverListener(this::onRotateSuggestionHover);
+ }
+ void registerListeners() {
+ if (mListenersRegistered) {
+ return;
+ }
+
+ mListenersRegistered = true;
try {
WindowManagerGlobal.getWindowManagerService()
.watchRotation(mRotationWatcher, mContext.getDisplay().getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
}
- void cleanUp() {
- // Unregister the task stack listener
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ void unregisterListeners() {
+ if (!mListenersRegistered) {
+ return;
+ }
+ mListenersRegistered = false;
try {
WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(mRotationWatcher);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
}
void addRotationCallback(Consumer<Integer> watcher) {
mRotWatcherListener = watcher;
}
- void setAccessibilityFeedbackEnabled(boolean flag) {
- mAccessibilityFeedbackEnabled = flag;
- }
-
void setRotationLockedAtAngle(int rotationSuggestion) {
mRotationLockController.setRotationLockedAtAngle(true /* locked */, rotationSuggestion);
}
@@ -185,7 +191,7 @@
// Clear any pending suggestion flag as it has either been nullified or is being shown
mPendingRotationSuggestion = false;
- view.removeCallbacks(mCancelPendingRotationProposal);
+ mMainThreadHandler.removeCallbacks(mCancelPendingRotationProposal);
// Handle the visibility change and animation
if (visible) { // Appear and change (cannot force)
@@ -255,13 +261,9 @@
return;
}
- final View currentView = mRotationButton.getCurrentView();
-
// If window rotation matches suggested rotation, remove any current suggestions
if (rotation == windowRotation) {
- if (currentView != null) {
- currentView.removeCallbacks(mRemoveRotationProposal);
- }
+ mMainThreadHandler.removeCallbacks(mRemoveRotationProposal);
setRotateSuggestionButtonState(false /* visible */);
return;
}
@@ -285,11 +287,9 @@
// If the navbar isn't shown, flag the rotate icon to be shown should the navbar become
// visible given some time limit.
mPendingRotationSuggestion = true;
- if (currentView != null) {
- currentView.removeCallbacks(mCancelPendingRotationProposal);
- currentView.postDelayed(mCancelPendingRotationProposal,
- NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS);
- }
+ mMainThreadHandler.removeCallbacks(mCancelPendingRotationProposal);
+ mMainThreadHandler.postDelayed(mCancelPendingRotationProposal,
+ NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS);
}
}
@@ -334,9 +334,7 @@
private void onRotationSuggestionsDisabled() {
// Immediately hide the rotate button and clear any planned removal
setRotateSuggestionButtonState(false /* visible */, true /* force */);
- if (mRotationButton.getCurrentView() != null) {
- mRotationButton.getCurrentView().removeCallbacks(mRemoveRotationProposal);
- }
+ mMainThreadHandler.removeCallbacks(mRemoveRotationProposal);
}
private void showAndLogRotationSuggestion() {
@@ -369,10 +367,6 @@
}
private void rescheduleRotationTimeout(final boolean reasonHover) {
- if (mRotationButton.getCurrentView() == null) {
- return;
- }
-
// May be called due to a new rotation proposal or a change in hover state
if (reasonHover) {
// Don't reschedule if a hide animator is running
@@ -382,16 +376,16 @@
}
// Stop any pending removal
- mRotationButton.getCurrentView().removeCallbacks(mRemoveRotationProposal);
+ mMainThreadHandler.removeCallbacks(mRemoveRotationProposal);
// Schedule timeout
- mRotationButton.getCurrentView().postDelayed(mRemoveRotationProposal,
+ mMainThreadHandler.postDelayed(mRemoveRotationProposal,
computeRotationProposalTimeout());
}
private int computeRotationProposalTimeout() {
- if (mAccessibilityFeedbackEnabled) return 10000;
- if (mHoveringRotationSuggestion) return 8000;
- return 5000;
+ return mAccessibilityManagerWrapper.getRecommendedTimeoutMillis(
+ mHoveringRotationSuggestion ? 16000 : 5000,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
}
private boolean isRotateSuggestionIntroduced() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
index 24e7336..bd96752 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
@@ -64,13 +64,6 @@
}
@Override
- public void onDestroy() {
- if (mRotationButtonController != null) {
- mRotationButtonController.cleanUp();
- }
- }
-
- @Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1fdabc0..cf07f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -493,7 +493,7 @@
private Runnable mLaunchTransitionEndRunnable;
private NotificationEntry mDraggedDownEntry;
- private boolean mLaunchCameraOnScreenTurningOn;
+ private boolean mLaunchCameraWhenFinishedWaking;
private boolean mLaunchCameraOnFinishedGoingToSleep;
private int mLastCameraLaunchSource;
protected PowerManager.WakeLock mGestureWakeLock;
@@ -3595,7 +3595,7 @@
public void onFinishedGoingToSleep() {
mNotificationPanel.onAffordanceLaunchEnded();
releaseGestureWakeLock();
- mLaunchCameraOnScreenTurningOn = false;
+ mLaunchCameraWhenFinishedWaking = false;
mDeviceInteractive = false;
mWakeUpComingFromTouch = false;
mWakeUpTouchLocation = null;
@@ -3640,6 +3640,11 @@
@Override
public void onFinishedWakingUp() {
mWakeUpCoordinator.setWakingUp(false);
+ if (mLaunchCameraWhenFinishedWaking) {
+ mNotificationPanel.launchCamera(false /* animate */, mLastCameraLaunchSource);
+ mLaunchCameraWhenFinishedWaking = false;
+ }
+ updateScrimController();
}
};
@@ -3660,13 +3665,6 @@
public void onScreenTurningOn() {
mFalsingManager.onScreenTurningOn();
mNotificationPanel.onScreenTurningOn();
-
- if (mLaunchCameraOnScreenTurningOn) {
- mNotificationPanel.launchCamera(false, mLastCameraLaunchSource);
- mLaunchCameraOnScreenTurningOn = false;
- }
-
- updateScrimController();
}
@Override
@@ -3761,7 +3759,7 @@
// comes on.
mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
- if (isScreenTurningOnOrOn()) {
+ if (isWakingUpOrAwake()) {
if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera");
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.reset(true /* hide */);
@@ -3774,7 +3772,7 @@
// incorrectly get notified because of the screen on event (which resumes and pauses
// some activities)
if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Deferring until screen turns on");
- mLaunchCameraOnScreenTurningOn = true;
+ mLaunchCameraWhenFinishedWaking = true;
}
}
}
@@ -3799,9 +3797,9 @@
== WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
}
- private boolean isScreenTurningOnOrOn() {
- return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_TURNING_ON
- || mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
+ private boolean isWakingUpOrAwake() {
+ return mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
+ || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING;
}
public void notifyBiometricAuthModeChanged() {
@@ -3833,7 +3831,7 @@
ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
mScrimController.transitionTo(state);
- } else if (isInLaunchTransition() || mLaunchCameraOnScreenTurningOn
+ } else if (isInLaunchTransition() || mLaunchCameraWhenFinishedWaking
|| launchingAffordanceWithPreview) {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
} else if (mBrightnessMirrorVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
new file mode 100644
index 0000000..f33ff27
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+package com.android.systemui.statusbar.phone;
+
+public interface StatusBarWindowCallback {
+ void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index c73ed60..8621b72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -18,9 +18,6 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import android.app.ActivityManager;
@@ -47,17 +44,19 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.RemoteInputController.Callback;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.google.android.collect.Lists;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
+import java.util.ArrayList;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -83,6 +82,8 @@
private float mScreenBrightnessDoze;
private final State mCurrentState = new State();
private OtherwisedCollapsedListener mListener;
+ private final ArrayList<WeakReference<StatusBarWindowCallback>>
+ mCallbacks = Lists.newArrayList();
private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
@@ -108,6 +109,19 @@
Dependency.get(ConfigurationController.class).addCallback(this);
}
+ /**
+ * Register to receive notifications about status bar window state changes.
+ */
+ public void registerCallback(StatusBarWindowCallback callback) {
+ // Prevent adding duplicate callbacks
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ if (mCallbacks.get(i).get() == callback) {
+ return;
+ }
+ }
+ mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback));
+ }
+
private boolean shouldEnableKeyguardScreenRotation() {
Resources res = mContext.getResources();
return SystemProperties.getBoolean("lockscreen.rot_override", false)
@@ -318,18 +332,18 @@
}
mHasTopUi = mHasTopUiChanged;
}
- updateSystemUiStateFlags();
+ notifyStateChangedCallbacks();
}
- public void updateSystemUiStateFlags() {
- int displayId = mContext.getDisplayId();
- OverviewProxyService overviewProxyService = Dependency.get(OverviewProxyService.class);
- overviewProxyService.setSystemUiStateFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
- mCurrentState.keyguardShowing && !mCurrentState.keyguardOccluded, displayId);
- overviewProxyService.setSystemUiStateFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
- mCurrentState.keyguardShowing && mCurrentState.keyguardOccluded, displayId);
- overviewProxyService.setSystemUiStateFlag(SYSUI_STATE_BOUNCER_SHOWING,
- mCurrentState.bouncerShowing, displayId);
+ public void notifyStateChangedCallbacks() {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ StatusBarWindowCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onStateChanged(mCurrentState.keyguardShowing,
+ mCurrentState.keyguardOccluded,
+ mCurrentState.bouncerShowing);
+ }
+ }
}
private void applyForceStatusBarVisibleFlag(State state) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 64b2842..c2f246f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -249,13 +249,8 @@
x = (int)ev.getRawX();
y = (int)ev.getRawY();
- boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > (mIsVertical
- ? QuickStepContract.getQuickScrubTouchSlopPx()
- : QuickStepContract.getQuickStepTouchSlopPx());
- boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > (mIsVertical
- ? QuickStepContract.getQuickStepTouchSlopPx()
- : QuickStepContract.getQuickScrubTouchSlopPx());
- if (exceededTouchSlopX || exceededTouchSlopY) {
+ float slop = QuickStepContract.getQuickStepTouchSlopPx(getContext());
+ if (Math.abs(x - mTouchDownX) > slop || Math.abs(y - mTouchDownY) > slop) {
// When quick step is enabled, prevent animating the ripple triggered by
// setPressed and decide to run it on touch up
setPressed(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
new file mode 100644
index 0000000..ade5f36
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
+import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+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)
+@TestableLooper.RunWithLooper
+public class DiagonalClassifierTest extends SysuiTestCase {
+
+ // Next variable is not actually five, but is very close. 5 degrees is currently the value
+ // used in the diagonal classifier, so we want slightly less than that to deal with
+ // floating point errors.
+ private static final float FIVE_DEG_IN_RADIANS = (float) (4.99f / 360f * Math.PI * 2f);
+ private static final float UP_IN_RADIANS = (float) (Math.PI / 2f);
+ private static final float DOWN_IN_RADIANS = (float) (3 * Math.PI / 2f);
+ private static final float RIGHT_IN_RADIANS = 0;
+ private static final float LEFT_IN_RADIANS = (float) Math.PI;
+ private static final float FORTY_FIVE_DEG_IN_RADIANS = (float) (Math.PI / 4);
+
+ @Mock
+ private FalsingDataProvider mDataProvider;
+ private FalsingClassifier mClassifier;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mClassifier = new DiagonalClassifier(mDataProvider);
+ }
+
+ @Test
+ public void testPass_UnknownAngle() {
+ when(mDataProvider.getAngle()).thenReturn(Float.MAX_VALUE);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_VerticalSwipe() {
+ when(mDataProvider.getAngle()).thenReturn(UP_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(DOWN_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_MostlyVerticalSwipe() {
+ when(mDataProvider.getAngle()).thenReturn(UP_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(UP_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(DOWN_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(DOWN_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS * 2);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_BarelyVerticalSwipe() {
+ when(mDataProvider.getAngle()).thenReturn(
+ UP_IN_RADIANS - FORTY_FIVE_DEG_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(
+ UP_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(
+ DOWN_IN_RADIANS - FORTY_FIVE_DEG_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(
+ DOWN_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS * 2);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_HorizontalSwipe() {
+ when(mDataProvider.getAngle()).thenReturn(RIGHT_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(LEFT_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_MostlyHorizontalSwipe() {
+ when(mDataProvider.getAngle()).thenReturn(RIGHT_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(RIGHT_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(LEFT_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(LEFT_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_BarelyHorizontalSwipe() {
+ when(mDataProvider.getAngle()).thenReturn(
+ RIGHT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(
+ LEFT_IN_RADIANS - FORTY_FIVE_DEG_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(
+ LEFT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getAngle()).thenReturn(
+ RIGHT_IN_RADIANS - FORTY_FIVE_DEG_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS * 2);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_AffordanceSwipe() {
+ when(mDataProvider.getInteractionType()).thenReturn(LEFT_AFFORDANCE);
+ when(mDataProvider.getAngle()).thenReturn(
+ RIGHT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.getInteractionType()).thenReturn(RIGHT_AFFORDANCE);
+ when(mDataProvider.getAngle()).thenReturn(
+ LEFT_IN_RADIANS - FORTY_FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ // This classifier may return false for other angles, but these are the only
+ // two that actually matter, as affordances generally only travel in these two directions.
+ // We expect other classifiers to false in those cases, so it really doesn't matter what
+ // we do here.
+ }
+
+ @Test
+ public void testFail_DiagonalSwipe() {
+ // Horizontal Swipes
+ when(mDataProvider.isVertical()).thenReturn(false);
+ when(mDataProvider.getAngle()).thenReturn(
+ RIGHT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.getAngle()).thenReturn(
+ UP_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS + FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.getAngle()).thenReturn(
+ LEFT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.getAngle()).thenReturn(
+ DOWN_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS + FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ // Vertical Swipes
+ when(mDataProvider.isVertical()).thenReturn(true);
+ when(mDataProvider.getAngle()).thenReturn(
+ RIGHT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS + FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.getAngle()).thenReturn(
+ UP_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+
+ when(mDataProvider.getAngle()).thenReturn(
+ LEFT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS + FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.getAngle()).thenReturn(
+ DOWN_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - FIVE_DEG_IN_RADIANS);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
new file mode 100644
index 0000000..3d0471b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DistanceClassifierTest extends SysuiTestCase {
+
+ @Mock
+ private FalsingDataProvider mDataProvider;
+ private FalsingClassifier mClassifier;
+ private List<MotionEvent> mMotionEvents = new ArrayList<>();
+
+ private static final float DPI = 100;
+ private static final int SCREEN_SIZE = (int) (DPI * 10);
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mDataProvider.getHeightPixels()).thenReturn(SCREEN_SIZE);
+ when(mDataProvider.getWidthPixels()).thenReturn(SCREEN_SIZE);
+ when(mDataProvider.getXdpi()).thenReturn(DPI);
+ when(mDataProvider.getYdpi()).thenReturn(DPI);
+ mClassifier = new DistanceClassifier(mDataProvider);
+ }
+
+ @Test
+ public void testPass_noPointer() {
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testPass_fling() {
+ MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
+ MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 2, 0);
+ MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_UP, 1, 40, 0);
+
+ appendMotionEvent(motionEventA);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ appendMotionEvent(motionEventB);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ appendMotionEvent(motionEventC);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ motionEventA.recycle();
+ motionEventB.recycle();
+ motionEventC.recycle();
+ }
+
+ @Test
+ public void testFail_flingShort() {
+ MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
+ MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 2, 0);
+ MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_UP, 1, 10, 0);
+
+ appendMotionEvent(motionEventA);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ appendMotionEvent(motionEventB);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ appendMotionEvent(motionEventC);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ motionEventA.recycle();
+ motionEventB.recycle();
+ motionEventC.recycle();
+ }
+
+ @Test
+ public void testFail_flingSlowly() {
+ // These events, in testing, result in a fling that falls just short of the threshold.
+ MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
+ MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 15, 0);
+ MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_MOVE, 1, 16, 0);
+ MotionEvent motionEventD = MotionEvent.obtain(1, 300, MotionEvent.ACTION_MOVE, 1, 17, 0);
+ MotionEvent motionEventE = MotionEvent.obtain(1, 301, MotionEvent.ACTION_MOVE, 1, 18, 0);
+ MotionEvent motionEventF = MotionEvent.obtain(1, 500, MotionEvent.ACTION_UP, 1, 19, 0);
+
+ appendMotionEvent(motionEventA);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ appendMotionEvent(motionEventB);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ appendMotionEvent(motionEventC);
+ appendMotionEvent(motionEventD);
+ appendMotionEvent(motionEventE);
+ appendMotionEvent(motionEventF);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ motionEventA.recycle();
+ motionEventB.recycle();
+ motionEventC.recycle();
+ motionEventD.recycle();
+ motionEventE.recycle();
+ motionEventF.recycle();
+ }
+
+ @Test
+ public void testPass_swipe() {
+ MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
+ MotionEvent motionEventB = MotionEvent.obtain(1, 3, MotionEvent.ACTION_MOVE, 1, DPI * 3, 0);
+ MotionEvent motionEventC = MotionEvent.obtain(1, 1000, MotionEvent.ACTION_UP, 1, DPI * 3,
+ 0);
+
+ appendMotionEvent(motionEventA);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+
+ appendMotionEvent(motionEventB);
+ appendMotionEvent(motionEventC);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ motionEventA.recycle();
+ motionEventB.recycle();
+ motionEventC.recycle();
+ }
+
+ private void appendMotionEvent(MotionEvent motionEvent) {
+ if (mMotionEvents.isEmpty()) {
+ when(mDataProvider.getFirstRecentMotionEvent()).thenReturn(motionEvent);
+ }
+
+ mMotionEvents.add(motionEvent);
+ when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents);
+
+ when(mDataProvider.getLastMotionEvent()).thenReturn(motionEvent);
+
+ mClassifier.onTouchEvent(motionEvent);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
new file mode 100644
index 0000000..1da4206
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
@@ -0,0 +1,293 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.closeTo;
+import static org.junit.Assert.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class FalsingDataProviderTest extends SysuiTestCase {
+
+ private FalsingDataProvider mDataProvider;
+
+ @Before
+ public void setup() {
+ mDataProvider = new FalsingDataProvider(getContext());
+ }
+
+ @Test
+ public void test_trackMotionEvents() {
+ MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 2, 9);
+ MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 4, 7);
+ MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_UP, 3, 6, 5);
+
+ mDataProvider.onMotionEvent(motionEventA);
+ mDataProvider.onMotionEvent(motionEventB);
+ mDataProvider.onMotionEvent(motionEventC);
+ List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
+
+ assertThat(motionEventList.size(), is(3));
+ assertThat(motionEventList.get(0).getActionMasked(), is(MotionEvent.ACTION_DOWN));
+ assertThat(motionEventList.get(1).getActionMasked(), is(MotionEvent.ACTION_MOVE));
+ assertThat(motionEventList.get(2).getActionMasked(), is(MotionEvent.ACTION_UP));
+ assertThat(motionEventList.get(0).getEventTime(), is(1L));
+ assertThat(motionEventList.get(1).getEventTime(), is(2L));
+ assertThat(motionEventList.get(2).getEventTime(), is(3L));
+ assertThat(motionEventList.get(0).getX(), is(2f));
+ assertThat(motionEventList.get(1).getX(), is(4f));
+ assertThat(motionEventList.get(2).getX(), is(6f));
+ assertThat(motionEventList.get(0).getY(), is(9f));
+ assertThat(motionEventList.get(1).getY(), is(7f));
+ assertThat(motionEventList.get(2).getY(), is(5f));
+
+ motionEventA.recycle();
+ motionEventB.recycle();
+ motionEventC.recycle();
+ }
+
+ @Test
+ public void test_trackRecentMotionEvents() {
+ MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 2, 9);
+ MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 800, 4, 7);
+ MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_UP, 1200, 6, 5);
+
+ mDataProvider.onMotionEvent(motionEventA);
+ mDataProvider.onMotionEvent(motionEventB);
+ List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
+
+ assertThat(motionEventList.size(), is(2));
+ assertThat(motionEventList.get(0).getActionMasked(), is(MotionEvent.ACTION_DOWN));
+ assertThat(motionEventList.get(1).getActionMasked(), is(MotionEvent.ACTION_MOVE));
+ assertThat(motionEventList.get(0).getEventTime(), is(1L));
+ assertThat(motionEventList.get(1).getEventTime(), is(800L));
+ assertThat(motionEventList.get(0).getX(), is(2f));
+ assertThat(motionEventList.get(1).getX(), is(4f));
+ assertThat(motionEventList.get(0).getY(), is(9f));
+ assertThat(motionEventList.get(1).getY(), is(7f));
+
+ mDataProvider.onMotionEvent(motionEventC);
+
+ // Still two events, but event a is gone.
+ assertThat(motionEventList.size(), is(2));
+ assertThat(motionEventList.get(0).getActionMasked(), is(MotionEvent.ACTION_MOVE));
+ assertThat(motionEventList.get(1).getActionMasked(), is(MotionEvent.ACTION_UP));
+ assertThat(motionEventList.get(0).getEventTime(), is(800L));
+ assertThat(motionEventList.get(1).getEventTime(), is(1200L));
+ assertThat(motionEventList.get(0).getX(), is(4f));
+ assertThat(motionEventList.get(1).getX(), is(6f));
+ assertThat(motionEventList.get(0).getY(), is(7f));
+ assertThat(motionEventList.get(1).getY(), is(5f));
+
+ // The first, real event should still be a, however.
+ MotionEvent firstRealMotionEvent = mDataProvider.getFirstActualMotionEvent();
+ assertThat(firstRealMotionEvent.getActionMasked(), is(MotionEvent.ACTION_DOWN));
+ assertThat(firstRealMotionEvent.getEventTime(), is(1L));
+ assertThat(firstRealMotionEvent.getX(), is(2f));
+ assertThat(firstRealMotionEvent.getY(), is(9f));
+
+ motionEventA.recycle();
+ motionEventB.recycle();
+ motionEventC.recycle();
+ }
+
+ @Test
+ public void test_unpackMotionEvents() {
+ // Batching only works for motion events of the same type.
+ MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 1, 2, 9);
+ MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 4, 7);
+ MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 3, 6, 5);
+ motionEventA.addBatch(motionEventB);
+ motionEventA.addBatch(motionEventC);
+ // Note that calling addBatch changes properties on the original event, not just it's
+ // historical artifacts.
+
+ mDataProvider.onMotionEvent(motionEventA);
+ List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
+
+ assertThat(motionEventList.size(), is(3));
+ assertThat(motionEventList.get(0).getActionMasked(), is(MotionEvent.ACTION_MOVE));
+ assertThat(motionEventList.get(1).getActionMasked(), is(MotionEvent.ACTION_MOVE));
+ assertThat(motionEventList.get(2).getActionMasked(), is(MotionEvent.ACTION_MOVE));
+ assertThat(motionEventList.get(0).getEventTime(), is(1L));
+ assertThat(motionEventList.get(1).getEventTime(), is(2L));
+ assertThat(motionEventList.get(2).getEventTime(), is(3L));
+ assertThat(motionEventList.get(0).getX(), is(2f));
+ assertThat(motionEventList.get(1).getX(), is(4f));
+ assertThat(motionEventList.get(2).getX(), is(6f));
+ assertThat(motionEventList.get(0).getY(), is(9f));
+ assertThat(motionEventList.get(1).getY(), is(7f));
+ assertThat(motionEventList.get(2).getY(), is(5f));
+
+ motionEventA.recycle();
+ motionEventB.recycle();
+ motionEventC.recycle();
+ }
+
+ @Test
+ public void test_getAngle() {
+ MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+
+ MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventA);
+ assertThat((double) mDataProvider.getAngle(), closeTo(Math.PI / 4, .001));
+ motionEventA.recycle();
+ mDataProvider.onSessionEnd();
+
+ MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -1, -1);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventB);
+ assertThat((double) mDataProvider.getAngle(), closeTo(5 * Math.PI / 4, .001));
+ motionEventB.recycle();
+ mDataProvider.onSessionEnd();
+
+
+ MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 2, 0);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventC);
+ assertThat((double) mDataProvider.getAngle(), closeTo(0, .001));
+ motionEventC.recycle();
+ mDataProvider.onSessionEnd();
+ }
+
+ @Test
+ public void test_isHorizontal() {
+ MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+
+ MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventA);
+ assertThat(mDataProvider.isHorizontal(), is(false));
+ motionEventA.recycle();
+ mDataProvider.onSessionEnd();
+
+ MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 2, 1);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventB);
+ assertThat(mDataProvider.isHorizontal(), is(true));
+ motionEventB.recycle();
+ mDataProvider.onSessionEnd();
+
+ MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -1);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventC);
+ assertThat(mDataProvider.isHorizontal(), is(true));
+ motionEventC.recycle();
+ mDataProvider.onSessionEnd();
+ }
+
+ @Test
+ public void test_isVertical() {
+ MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+
+ MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 0);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventA);
+ assertThat(mDataProvider.isVertical(), is(false));
+ motionEventA.recycle();
+ mDataProvider.onSessionEnd();
+
+ MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 1);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventB);
+ assertThat(mDataProvider.isVertical(), is(true));
+ motionEventB.recycle();
+ mDataProvider.onSessionEnd();
+
+ MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -10);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventC);
+ assertThat(mDataProvider.isVertical(), is(true));
+ motionEventC.recycle();
+ mDataProvider.onSessionEnd();
+ }
+
+ @Test
+ public void test_isRight() {
+ MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+
+ MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventA);
+ assertThat(mDataProvider.isRight(), is(true));
+ motionEventA.recycle();
+ mDataProvider.onSessionEnd();
+
+ MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 1);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventB);
+ assertThat(mDataProvider.isRight(), is(false));
+ motionEventB.recycle();
+ mDataProvider.onSessionEnd();
+
+ MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -10);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventC);
+ assertThat(mDataProvider.isRight(), is(false));
+ motionEventC.recycle();
+ mDataProvider.onSessionEnd();
+ }
+
+ @Test
+ public void test_isUp() {
+ // Remember that our y axis is flipped.
+
+ MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+
+ MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, -1);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventA);
+ assertThat(mDataProvider.isUp(), is(true));
+ motionEventA.recycle();
+ mDataProvider.onSessionEnd();
+
+ MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 0);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventB);
+ assertThat(mDataProvider.isUp(), is(false));
+ motionEventB.recycle();
+ mDataProvider.onSessionEnd();
+
+ MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, 10);
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(motionEventC);
+ assertThat(mDataProvider.isUp(), is(false));
+ motionEventC.recycle();
+ mDataProvider.onSessionEnd();
+ }
+
+ private MotionEvent obtainMotionEvent(int action, long eventTimeMs, float x, float y) {
+ return MotionEvent.obtain(1, eventTimeMs, action, x, y, 0);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
new file mode 100644
index 0000000..cba9ee38
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PointerCountClassifierTest extends SysuiTestCase {
+
+ @Mock
+ private FalsingDataProvider mDataProvider;
+ private FalsingClassifier mClassifier;
+
+ @Before
+ public void setup() {
+ mClassifier = new PointerCountClassifier(mDataProvider);
+ }
+
+ @Test
+ public void testPass_noPointer() {
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_singlePointer() {
+ MotionEvent motionEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
+ mClassifier.onTouchEvent(motionEvent);
+ motionEvent.recycle();
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testFail_multiPointer() {
+ MotionEvent.PointerProperties[] pointerProperties =
+ MotionEvent.PointerProperties.createArray(2);
+ pointerProperties[0].id = 0;
+ pointerProperties[1].id = 1;
+ MotionEvent.PointerCoords[] pointerCoords = MotionEvent.PointerCoords.createArray(2);
+ MotionEvent motionEvent = MotionEvent.obtain(
+ 1, 1, MotionEvent.ACTION_DOWN, 2, pointerProperties, pointerCoords, 0, 0, 0, 0, 0,
+ 0,
+ 0, 0);
+ mClassifier.onTouchEvent(motionEvent);
+ motionEvent.recycle();
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
new file mode 100644
index 0000000..2ed7925
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import static com.android.systemui.classifier.Classifier.GENERIC;
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.Field;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ProximityClassifierTest extends SysuiTestCase {
+
+ private static final long NS_PER_MS = 1000000;
+
+ @Mock
+ private FalsingDataProvider mDataProvider;
+ @Mock
+ private DistanceClassifier mDistanceClassifier;
+ private FalsingClassifier mClassifier;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mDataProvider.getInteractionType()).thenReturn(GENERIC);
+ when(mDistanceClassifier.isLongSwipe()).thenReturn(false);
+ mClassifier = new ProximityClassifier(mDistanceClassifier, mDataProvider);
+ }
+
+ @Test
+ public void testPass_uncovered() {
+ touchDown();
+ touchUp(10);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_mostlyUncovered() {
+ touchDown();
+ mClassifier.onSensorEvent(createSensorEvent(true, 1));
+ mClassifier.onSensorEvent(createSensorEvent(false, 2));
+ touchUp(20);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_quickSettings() {
+ touchDown();
+ when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS);
+ mClassifier.onSensorEvent(createSensorEvent(true, 1));
+ mClassifier.onSensorEvent(createSensorEvent(false, 11));
+ touchUp(10);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testFail_covered() {
+ touchDown();
+ mClassifier.onSensorEvent(createSensorEvent(true, 1));
+ mClassifier.onSensorEvent(createSensorEvent(false, 11));
+ touchUp(10);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testFail_mostlyCovered() {
+ touchDown();
+ mClassifier.onSensorEvent(createSensorEvent(true, 1));
+ mClassifier.onSensorEvent(createSensorEvent(true, 95));
+ mClassifier.onSensorEvent(createSensorEvent(true, 96));
+ mClassifier.onSensorEvent(createSensorEvent(false, 100));
+ touchUp(100);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testPass_coveredWithLongSwipe() {
+ touchDown();
+ mClassifier.onSensorEvent(createSensorEvent(true, 1));
+ mClassifier.onSensorEvent(createSensorEvent(false, 11));
+ touchUp(10);
+ when(mDistanceClassifier.isLongSwipe()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ private void touchDown() {
+ MotionEvent motionEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ mClassifier.onTouchEvent(motionEvent);
+ motionEvent.recycle();
+ }
+
+ private void touchUp(long duration) {
+ MotionEvent motionEvent = MotionEvent.obtain(1, 1 + duration, MotionEvent.ACTION_UP, 0,
+ 100, 0);
+
+ mClassifier.onTouchEvent(motionEvent);
+
+ motionEvent.recycle();
+ }
+
+ private SensorEvent createSensorEvent(boolean covered, long timestampMs) {
+ SensorEvent sensorEvent = Mockito.mock(SensorEvent.class);
+ Sensor sensor = Mockito.mock(Sensor.class);
+ when(sensor.getType()).thenReturn(Sensor.TYPE_PROXIMITY);
+ when(sensor.getMaximumRange()).thenReturn(1f);
+ sensorEvent.sensor = sensor;
+ sensorEvent.timestamp = timestampMs * NS_PER_MS;
+ try {
+ Field valuesField = SensorEvent.class.getField("values");
+ valuesField.setAccessible(true);
+ float[] sensorValue = {covered ? 0 : 1};
+ try {
+ valuesField.set(sensorEvent, sensorValue);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ }
+
+ return sensorEvent;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
new file mode 100644
index 0000000..4bb3c15
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
@@ -0,0 +1,307 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
+import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
+import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
+import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
+import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
+import static com.android.systemui.classifier.Classifier.UNLOCK;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+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)
+@TestableLooper.RunWithLooper
+public class TypeClassifierTest extends SysuiTestCase {
+
+ @Mock
+ private FalsingDataProvider mDataProvider;
+
+ private FalsingClassifier mClassifier;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mClassifier = new TypeClassifier(mDataProvider);
+ }
+
+ @Test
+ public void testPass_QuickSettings() {
+ when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS);
+ when(mDataProvider.isVertical()).thenReturn(true);
+ when(mDataProvider.isUp()).thenReturn(false);
+
+ when(mDataProvider.isRight()).thenReturn(false); // right should cause no effect.
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testFalse_QuickSettings() {
+ when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS);
+
+ when(mDataProvider.isVertical()).thenReturn(false);
+ when(mDataProvider.isUp()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isVertical()).thenReturn(true);
+ when(mDataProvider.isUp()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testPass_PulseExpand() {
+ when(mDataProvider.getInteractionType()).thenReturn(PULSE_EXPAND);
+ when(mDataProvider.isVertical()).thenReturn(true);
+ when(mDataProvider.isUp()).thenReturn(false);
+
+ when(mDataProvider.isRight()).thenReturn(false); // right should cause no effect.
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testFalse_PulseExpand() {
+ when(mDataProvider.getInteractionType()).thenReturn(PULSE_EXPAND);
+
+ when(mDataProvider.isVertical()).thenReturn(false);
+ when(mDataProvider.isUp()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isVertical()).thenReturn(true);
+ when(mDataProvider.isUp()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testPass_NotificationDragDown() {
+ when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DRAG_DOWN);
+ when(mDataProvider.isVertical()).thenReturn(true);
+ when(mDataProvider.isUp()).thenReturn(false);
+
+ when(mDataProvider.isRight()).thenReturn(false); // right should cause no effect.
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testFalse_NotificationDragDown() {
+ when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DRAG_DOWN);
+
+ when(mDataProvider.isVertical()).thenReturn(false);
+ when(mDataProvider.isUp()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isVertical()).thenReturn(true);
+ when(mDataProvider.isUp()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testPass_NotificationDismiss() {
+ when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DISMISS);
+ when(mDataProvider.isVertical()).thenReturn(false);
+
+ when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testFalse_NotificationDismiss() {
+ when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DISMISS);
+ when(mDataProvider.isVertical()).thenReturn(true);
+
+ when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+
+ @Test
+ public void testPass_Unlock() {
+ when(mDataProvider.getInteractionType()).thenReturn(UNLOCK);
+ when(mDataProvider.isVertical()).thenReturn(true);
+ when(mDataProvider.isUp()).thenReturn(true);
+
+
+ when(mDataProvider.isRight()).thenReturn(false); // right should cause no effect.
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testFalse_Unlock() {
+ when(mDataProvider.getInteractionType()).thenReturn(UNLOCK);
+
+ when(mDataProvider.isVertical()).thenReturn(false);
+ when(mDataProvider.isUp()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isVertical()).thenReturn(true);
+ when(mDataProvider.isUp()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isVertical()).thenReturn(false);
+ when(mDataProvider.isUp()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testPass_BouncerUnlock() {
+ when(mDataProvider.getInteractionType()).thenReturn(BOUNCER_UNLOCK);
+ when(mDataProvider.isVertical()).thenReturn(true);
+ when(mDataProvider.isUp()).thenReturn(true);
+
+
+ when(mDataProvider.isRight()).thenReturn(false); // right should cause no effect.
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testFalse_BouncerUnlock() {
+ when(mDataProvider.getInteractionType()).thenReturn(BOUNCER_UNLOCK);
+
+ when(mDataProvider.isVertical()).thenReturn(false);
+ when(mDataProvider.isUp()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isVertical()).thenReturn(true);
+ when(mDataProvider.isUp()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isVertical()).thenReturn(false);
+ when(mDataProvider.isUp()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testPass_LeftAffordance() {
+ when(mDataProvider.getInteractionType()).thenReturn(LEFT_AFFORDANCE);
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+
+
+ when(mDataProvider.isVertical()).thenReturn(false); // vertical should cause no effect.
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.isVertical()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testFalse_LeftAffordance() {
+ when(mDataProvider.getInteractionType()).thenReturn(LEFT_AFFORDANCE);
+
+ when(mDataProvider.isRight()).thenReturn(false);
+ when(mDataProvider.isUp()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isRight()).thenReturn(true);
+ when(mDataProvider.isUp()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isRight()).thenReturn(false);
+ when(mDataProvider.isUp()).thenReturn(false);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testPass_RightAffordance() {
+ when(mDataProvider.getInteractionType()).thenReturn(RIGHT_AFFORDANCE);
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(false);
+
+
+ when(mDataProvider.isVertical()).thenReturn(false); // vertical should cause no effect.
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ when(mDataProvider.isVertical()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testFalse_RightAffordance() {
+ when(mDataProvider.getInteractionType()).thenReturn(RIGHT_AFFORDANCE);
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
new file mode 100644
index 0000000..976b586
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
@@ -0,0 +1,467 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ZigZagClassifierTest extends SysuiTestCase {
+
+ private static final long NS_PER_MS = 1000000;
+
+ @Mock
+ private FalsingDataProvider mDataProvider;
+ private FalsingClassifier mClassifier;
+ private List<MotionEvent> mMotionEvents = new ArrayList<>();
+ private float mOffsetX = 0;
+ private float mOffsetY = 0;
+ private float mDx;
+ private float mDy;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mDataProvider.getXdpi()).thenReturn(100f);
+ when(mDataProvider.getYdpi()).thenReturn(100f);
+ when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents);
+ mClassifier = new ZigZagClassifier(mDataProvider);
+
+
+ // Calculate the response to these calls on the fly, otherwise Mockito gets bogged down
+ // everytime we call appendMotionEvent.
+ when(mDataProvider.getFirstRecentMotionEvent()).thenAnswer(
+ (Answer<MotionEvent>) invocation -> mMotionEvents.get(0));
+ when(mDataProvider.getLastMotionEvent()).thenAnswer(
+ (Answer<MotionEvent>) invocation -> mMotionEvents.get(mMotionEvents.size() - 1));
+ when(mDataProvider.isHorizontal()).thenAnswer(
+ (Answer<Boolean>) invocation -> Math.abs(mDy) < Math.abs(mDx));
+ when(mDataProvider.isVertical()).thenAnswer(
+ (Answer<Boolean>) invocation -> Math.abs(mDy) > Math.abs(mDx));
+ when(mDataProvider.isRight()).thenAnswer((Answer<Boolean>) invocation -> mDx > 0);
+ when(mDataProvider.isUp()).thenAnswer((Answer<Boolean>) invocation -> mDy < 0);
+ }
+
+ @After
+ public void tearDown() {
+ for (MotionEvent motionEvent : mMotionEvents) {
+ motionEvent.recycle();
+ }
+ mMotionEvents.clear();
+ }
+
+ @Test
+ public void testPass_fewTouchesVertical() {
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ appendMotionEvent(0, 0);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ appendMotionEvent(0, 100);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_vertical() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(0, 100);
+ appendMotionEvent(0, 200);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_fewTouchesHorizontal() {
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ appendMotionEvent(0, 0);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ appendMotionEvent(100, 0);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_horizontal() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(100, 0);
+ appendMotionEvent(200, 0);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+
+ @Test
+ public void testFail_minimumTouchesVertical() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(0, 100);
+ appendMotionEvent(0, 1);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testFail_minimumTouchesHorizontal() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(100, 0);
+ appendMotionEvent(1, 0);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testPass_fortyFiveDegreesStraight() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(10, 10);
+ appendMotionEvent(20, 20);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_horizontalZigZagVerticalStraight() {
+ // This test looks just like testFail_horizontalZigZagVerticalStraight but with
+ // a longer y range, making it look straighter.
+ appendMotionEvent(0, 0);
+ appendMotionEvent(5, 100);
+ appendMotionEvent(-5, 200);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testPass_horizontalStraightVerticalZigZag() {
+ // This test looks just like testFail_horizontalStraightVerticalZigZag but with
+ // a longer x range, making it look straighter.
+ appendMotionEvent(0, 0);
+ appendMotionEvent(100, 5);
+ appendMotionEvent(200, -5);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+ }
+
+ @Test
+ public void testFail_horizontalZigZagVerticalStraight() {
+ // This test looks just like testPass_horizontalZigZagVerticalStraight but with
+ // a shorter y range, making it look more crooked.
+ appendMotionEvent(0, 0);
+ appendMotionEvent(5, 10);
+ appendMotionEvent(-5, 20);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void testFail_horizontalStraightVerticalZigZag() {
+ // This test looks just like testPass_horizontalStraightVerticalZigZag but with
+ // a shorter x range, making it look more crooked.
+ appendMotionEvent(0, 0);
+ appendMotionEvent(10, 5);
+ appendMotionEvent(20, -5);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void test_between0And45() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(100, 5);
+ appendMotionEvent(200, 10);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(100, 0);
+ appendMotionEvent(200, 10);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(100, -10);
+ appendMotionEvent(200, 10);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(100, -10);
+ appendMotionEvent(200, 50);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void test_between45And90() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(10, 50);
+ appendMotionEvent(8, 100);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(1, 800);
+ appendMotionEvent(2, 900);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-10, 600);
+ appendMotionEvent(30, 700);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(40, 100);
+ appendMotionEvent(0, 101);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void test_between90And135() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-10, 50);
+ appendMotionEvent(-24, 100);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-20, 800);
+ appendMotionEvent(-20, 900);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(30, 600);
+ appendMotionEvent(-10, 700);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-80, 100);
+ appendMotionEvent(-10, 101);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void test_between135And180() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-120, 10);
+ appendMotionEvent(-200, 20);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-20, 8);
+ appendMotionEvent(-40, 2);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-500, -2);
+ appendMotionEvent(-600, 70);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-80, 100);
+ appendMotionEvent(-100, 1);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void test_between180And225() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-120, -10);
+ appendMotionEvent(-200, -20);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-20, -8);
+ appendMotionEvent(-40, -2);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-500, 2);
+ appendMotionEvent(-600, -70);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-80, -100);
+ appendMotionEvent(-100, -1);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void test_between225And270() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-12, -20);
+ appendMotionEvent(-20, -40);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-20, -130);
+ appendMotionEvent(-40, -260);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(1, -100);
+ appendMotionEvent(-6, -200);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-80, -100);
+ appendMotionEvent(-10, -110);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void test_between270And315() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(12, -20);
+ appendMotionEvent(20, -40);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(20, -130);
+ appendMotionEvent(40, -260);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(-1, -100);
+ appendMotionEvent(6, -200);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(80, -100);
+ appendMotionEvent(10, -110);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void test_between315And360() {
+ appendMotionEvent(0, 0);
+ appendMotionEvent(120, -20);
+ appendMotionEvent(200, -40);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(200, -13);
+ appendMotionEvent(400, -30);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(100, 10);
+ appendMotionEvent(600, -20);
+ assertThat(mClassifier.isFalseTouch(), is(false));
+
+ mMotionEvents.clear();
+ appendMotionEvent(0, 0);
+ appendMotionEvent(80, -100);
+ appendMotionEvent(100, -1);
+ assertThat(mClassifier.isFalseTouch(), is(true));
+ }
+
+ @Test
+ public void test_randomOrigins() {
+ // The purpose of this test is to try all the other tests from different starting points.
+ // We use a pre-determined seed to make this test repeatable.
+ Random rand = new Random(23);
+ for (int i = 0; i < 100; i++) {
+ mOffsetX = rand.nextInt(2000) - 1000;
+ mOffsetY = rand.nextInt(2000) - 1000;
+ try {
+ mMotionEvents.clear();
+ testPass_fewTouchesVertical();
+ mMotionEvents.clear();
+ testPass_vertical();
+ mMotionEvents.clear();
+ testFail_horizontalStraightVerticalZigZag();
+ mMotionEvents.clear();
+ testFail_horizontalZigZagVerticalStraight();
+ mMotionEvents.clear();
+ testFail_minimumTouchesHorizontal();
+ mMotionEvents.clear();
+ testFail_minimumTouchesVertical();
+ mMotionEvents.clear();
+ testPass_fewTouchesHorizontal();
+ mMotionEvents.clear();
+ testPass_fortyFiveDegreesStraight();
+ mMotionEvents.clear();
+ testPass_horizontal();
+ mMotionEvents.clear();
+ testPass_horizontalStraightVerticalZigZag();
+ mMotionEvents.clear();
+ testPass_horizontalZigZagVerticalStraight();
+ mMotionEvents.clear();
+ test_between0And45();
+ mMotionEvents.clear();
+ test_between45And90();
+ mMotionEvents.clear();
+ test_between90And135();
+ mMotionEvents.clear();
+ test_between135And180();
+ mMotionEvents.clear();
+ test_between180And225();
+ mMotionEvents.clear();
+ test_between225And270();
+ mMotionEvents.clear();
+ test_between270And315();
+ mMotionEvents.clear();
+ test_between315And360();
+ } catch (AssertionError e) {
+ throw new AssertionError("Random origin failure in iteration " + i, e);
+ }
+ }
+ }
+
+
+ private void appendMotionEvent(float x, float y) {
+ x += mOffsetX;
+ y += mOffsetY;
+
+ long eventTime = mMotionEvents.size() + 1;
+ MotionEvent motionEvent = MotionEvent.obtain(1, eventTime, MotionEvent.ACTION_DOWN, x, y,
+ 0);
+ mMotionEvents.add(motionEvent);
+
+ mDx = mDataProvider.getFirstRecentMotionEvent().getX()
+ - mDataProvider.getLastMotionEvent().getX();
+ mDy = mDataProvider.getFirstRecentMotionEvent().getY()
+ - mDataProvider.getLastMotionEvent().getY();
+
+ mClassifier.onTouchEvent(motionEvent);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index b99958a..73abda9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -31,6 +31,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -41,6 +42,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before;
import org.junit.Rule;
@@ -60,6 +62,7 @@
@Mock private NotificationStackScrollLayout mNssl;
@Mock private ActivityStarterDelegate mActivityStarterDelegate;
@Mock private StatusBarStateController mStatusBarStateController;
+ @Mock private ConfigurationController mConfigurationController;
private NotificationSectionsManager mSectionsManager;
@@ -70,15 +73,21 @@
mNssl,
mActivityStarterDelegate,
mStatusBarStateController,
+ mConfigurationController,
true);
// Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class)))
.thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- mSectionsManager.inflateViews(mContext);
+ mSectionsManager.initialize(LayoutInflater.from(mContext));
when(mNssl.indexOfChild(any(View.class))).thenReturn(-1);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
}
+ @Test(expected = IllegalStateException.class)
+ public void testDuplicateInitializeThrows() {
+ mSectionsManager.initialize(LayoutInflater.from(mContext));
+ }
+
@Test
public void testInsertHeader() {
// GIVEN a stack with HI and LO rows but no section headers
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index d425982..9f49e89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -75,6 +75,7 @@
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarTest.TestableNotificationEntryManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.After;
import org.junit.Assert;
@@ -155,6 +156,7 @@
true /* allowLongPress */, mNotificationRoundnessManager,
new AmbientPulseManager(mContext),
mock(DynamicPrivacyController.class),
+ mock(ConfigurationController.class),
mock(ActivityStarterDelegate.class),
mock(StatusBarStateController.class));
mStackScroller = spy(mStackScrollerInternal);
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 0f0e6f9..c58e929 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7388,6 +7388,20 @@
// CATEGORY: NOTIFICATION
MEDIA_NOTIFICATION_SEEKBAR = 1743;
+ // Custom tag for StatusBarNotification. Length of
+ // Notification.extras[EXTRA_PEOPLE_LIST], set by addPerson().
+ FIELD_NOTIFICATION_PEOPLE = 1744;
+
+ // Custom tag for StatusBarNotification. The Java hashcode of
+ // Notification.extras[EXTRA_TEMPLATE], which is a string like
+ // android.app.Notification$MessagingStyle, set by setStyle().
+ FIELD_NOTIFICATION_STYLE = 1745;
+
+ // OPEN: Settings > About phone > Legal information > Google Play system update licenses
+ // CATEGORY: SETTINGS
+ // OS: Q
+ MODULE_LICENSES_DASHBOARD = 1746;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto
index a1bbe52..381d983 100644
--- a/proto/src/task_snapshot.proto
+++ b/proto/src/task_snapshot.proto
@@ -32,4 +32,5 @@
int32 system_ui_visibility = 8;
bool is_translucent = 9;
string top_activity_component = 10;
+ float scale = 11;
}
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 0ce6c87..3143bcb 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -41,6 +41,8 @@
import com.android.internal.infra.AbstractSinglePendingRequestRemoteService;
+import java.util.concurrent.CompletableFuture;
+
final class RemoteFillService
extends AbstractSinglePendingRequestRemoteService<RemoteFillService, IAutoFillService> {
@@ -103,26 +105,21 @@
* <p>This can be used when the request is unnecessary or will be superceeded by a request that
* will soon be queued.
*
- * @return the id of the canceled request, or {@link FillRequest#INVALID_REQUEST_ID} if no
- * {@link PendingFillRequest} was canceled.
+ * @return the future id of the canceled request, or {@link FillRequest#INVALID_REQUEST_ID} if
+ * no {@link PendingFillRequest} was canceled.
*/
- // TODO(b/117779333): move this logic to super class (and make mPendingRequest private)
- public int cancelCurrentRequest() {
- if (isDestroyed()) {
- return INVALID_REQUEST_ID;
- }
-
- int requestId = INVALID_REQUEST_ID;
- if (mPendingRequest != null) {
- if (mPendingRequest instanceof PendingFillRequest) {
- requestId = ((PendingFillRequest) mPendingRequest).mRequest.getId();
+ public CompletableFuture<Integer> cancelCurrentRequest() {
+ return CompletableFuture.supplyAsync(() -> {
+ if (isDestroyed()) {
+ return INVALID_REQUEST_ID;
}
- mPendingRequest.cancel();
- mPendingRequest = null;
- }
-
- return requestId;
+ BasePendingRequest<RemoteFillService, IAutoFillService> canceledRequest =
+ handleCancelPendingRequest();
+ return canceledRequest instanceof PendingFillRequest
+ ? ((PendingFillRequest) canceledRequest).mRequest.getId()
+ : INVALID_REQUEST_ID;
+ }, mHandler::post);
}
public void onFillRequest(@NonNull FillRequest request) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3764ca4..895a2ae 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -546,21 +546,26 @@
+ "mForAugmentedAutofillOnly: %s", mForAugmentedAutofillOnly);
return;
}
- final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
+ mRemoteFillService.cancelCurrentRequest().whenComplete((canceledRequest, err) -> {
+ if (err != null) {
+ Slog.e(TAG, "cancelCurrentRequest(): unexpected exception", err);
+ return;
+ }
- // Remove the FillContext as there will never be a response for the service
- if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
- final int numContexts = mContexts.size();
+ // Remove the FillContext as there will never be a response for the service
+ if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
+ final int numContexts = mContexts.size();
- // It is most likely the last context, hence search backwards
- for (int i = numContexts - 1; i >= 0; i--) {
- if (mContexts.get(i).getRequestId() == canceledRequest) {
- if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
- mContexts.remove(i);
- break;
+ // It is most likely the last context, hence search backwards
+ for (int i = numContexts - 1; i >= 0; i--) {
+ if (mContexts.get(i).getRequestId() == canceledRequest) {
+ if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
+ mContexts.remove(i);
+ break;
+ }
}
}
- }
+ });
}
/**
@@ -2090,8 +2095,8 @@
updateValuesForSaveLocked();
// Remove pending fill requests as the session is finished.
- cancelCurrentRequestLocked();
+ cancelCurrentRequestLocked();
final ArrayList<FillContext> contexts = mergePreviousSessionLocked( /* forSave= */ true);
final SaveRequest saveRequest =
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index 593478c..06d9395 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -95,7 +95,7 @@
@GuardedBy("mLock")
void provideContextImageLocked(int taskId, @NonNull Bundle imageContextRequestExtras) {
- RemoteContentSuggestionsService service = getRemoteServiceLocked();
+ RemoteContentSuggestionsService service = ensureRemoteServiceLocked();
if (service != null) {
ActivityManager.TaskSnapshot snapshot =
mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false);
@@ -118,7 +118,7 @@
void suggestContentSelectionsLocked(
@NonNull SelectionsRequest selectionsRequest,
@NonNull ISelectionsCallback selectionsCallback) {
- RemoteContentSuggestionsService service = getRemoteServiceLocked();
+ RemoteContentSuggestionsService service = ensureRemoteServiceLocked();
if (service != null) {
service.suggestContentSelections(selectionsRequest, selectionsCallback);
}
@@ -128,7 +128,7 @@
void classifyContentSelectionsLocked(
@NonNull ClassificationsRequest classificationsRequest,
@NonNull IClassificationsCallback callback) {
- RemoteContentSuggestionsService service = getRemoteServiceLocked();
+ RemoteContentSuggestionsService service = ensureRemoteServiceLocked();
if (service != null) {
service.classifyContentSelections(classificationsRequest, callback);
}
@@ -136,7 +136,7 @@
@GuardedBy("mLock")
void notifyInteractionLocked(@NonNull String requestId, @NonNull Bundle bundle) {
- RemoteContentSuggestionsService service = getRemoteServiceLocked();
+ RemoteContentSuggestionsService service = ensureRemoteServiceLocked();
if (service != null) {
service.notifyInteraction(requestId, bundle);
}
@@ -153,12 +153,12 @@
@GuardedBy("mLock")
@Nullable
- private RemoteContentSuggestionsService getRemoteServiceLocked() {
+ private RemoteContentSuggestionsService ensureRemoteServiceLocked() {
if (mRemoteService == null) {
final String serviceName = getComponentNameLocked();
if (serviceName == null) {
if (mMaster.verbose) {
- Slog.v(TAG, "getRemoteServiceLocked(): not set");
+ Slog.v(TAG, "ensureRemoteServiceLocked(): not set");
}
return null;
}
@@ -170,8 +170,8 @@
@Override
public void onServiceDied(
@NonNull RemoteContentSuggestionsService service) {
- // TODO(b/120865921): properly implement
Slog.w(TAG, "remote content suggestions service died");
+ updateRemoteServiceLocked();
}
}, mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index e2a874e..d162441 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -3019,46 +3019,10 @@
DateFormat.format(pattern, info.getTriggerTime()).toString();
}
- /**
- * If the last time AlarmThread woke up precedes any due wakeup or non-wakeup alarm that we set
- * by more than half a minute, log a wtf.
- */
- private void validateLastAlarmExpiredLocked(long nowElapsed) {
- final StringBuilder errorMsg = new StringBuilder();
- boolean stuck = false;
- if (mNextNonWakeup < (nowElapsed - 10_000) && mLastWakeup < mNextNonWakeup) {
- stuck = true;
- errorMsg.append("[mNextNonWakeup=");
- TimeUtils.formatDuration(mNextNonWakeup - nowElapsed, errorMsg);
- errorMsg.append(" set at ");
- TimeUtils.formatDuration(mNextNonWakeUpSetAt - nowElapsed, errorMsg);
- errorMsg.append(", mLastWakeup=");
- TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg);
- errorMsg.append(", timerfd_gettime=" + mInjector.getNextAlarm(ELAPSED_REALTIME));
- errorMsg.append("];");
- }
- if (mNextWakeup < (nowElapsed - 10_000) && mLastWakeup < mNextWakeup) {
- stuck = true;
- errorMsg.append("[mNextWakeup=");
- TimeUtils.formatDuration(mNextWakeup - nowElapsed, errorMsg);
- errorMsg.append(" set at ");
- TimeUtils.formatDuration(mNextWakeUpSetAt - nowElapsed, errorMsg);
- errorMsg.append(", mLastWakeup=");
- TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg);
- errorMsg.append(", timerfd_gettime="
- + mInjector.getNextAlarm(ELAPSED_REALTIME_WAKEUP));
- errorMsg.append("];");
- }
- if (stuck) {
- Slog.wtf(TAG, "Alarm delivery stuck: " + errorMsg.toString());
- }
- }
-
void rescheduleKernelAlarmsLocked() {
// Schedule the next upcoming wakeup alarm. If there is a deliverable batch
// prior to that which contains no wakeups, we schedule that as well.
final long nowElapsed = mInjector.getElapsedRealtime();
- validateLastAlarmExpiredLocked(nowElapsed);
long nextNonWakeup = 0;
if (mAlarmBatches.size() > 0) {
final Batch firstWakeup = findFirstWakeupBatchLocked();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1cca0b9..e81d172 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -20,6 +20,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.NETID_UNSET;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
@@ -6954,6 +6955,12 @@
}
}
+ // restore private DNS settings to default mode (opportunistic)
+ if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS)) {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OPPORTUNISTIC);
+ }
+
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 0eb3a84..59589cd2 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -46,7 +46,9 @@
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
+import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings.Secure;
import android.service.dreams.Sandman;
import android.service.vr.IVrManager;
@@ -64,6 +66,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Objects;
final class UiModeManagerService extends SystemService {
private static final String TAG = UiModeManager.class.getSimpleName();
@@ -71,6 +74,7 @@
// Enable launching of applications when entering the dock.
private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
+ private static final String SYSTEM_PROPERTY_DEVICE_THEME = "persist.sys.theme";
final Object mLock = new Object();
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
@@ -330,8 +334,13 @@
mNightMode = defaultNightMode;
}
- // false if night mode stayed the same, true otherwise.
- return !(oldNightMode == mNightMode);
+ if (UserManager.get(context).isPrimaryUser()) {
+ final String newTheme = Integer.toString(mNightMode);
+ if (!Objects.equals(SystemProperties.get(SYSTEM_PROPERTY_DEVICE_THEME), mNightMode)) {
+ SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, newTheme);
+ }
+ }
+ return oldNightMode != mNightMode;
}
private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
@@ -411,6 +420,11 @@
try {
synchronized (mLock) {
if (mNightMode != mode) {
+ if (UserManager.get(getContext()).isPrimaryUser()) {
+ SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME,
+ Integer.toString(mode));
+ }
+
// Only persist setting if not in car mode
if (!mCarModeEnabled) {
Secure.putIntForUser(getContext().getContentResolver(),
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index fe95542..4595084 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -65,6 +65,8 @@
Context.BIND_VISIBLE,
Context.BIND_SHOWING_UI,
Context.BIND_NOT_VISIBLE,
+ Context.BIND_NOT_PERCEPTIBLE,
+ Context.BIND_INCLUDE_CAPABILITIES,
};
private static final int[] BIND_PROTO_ENUMS = new int[] {
ConnectionRecordProto.AUTO_CREATE,
@@ -82,6 +84,8 @@
ConnectionRecordProto.VISIBLE,
ConnectionRecordProto.SHOWING_UI,
ConnectionRecordProto.NOT_VISIBLE,
+ ConnectionRecordProto.NOT_PERCEPTIBLE,
+ ConnectionRecordProto.INCLUDE_CAPABILITIES,
};
void dump(PrintWriter pw, String prefix) {
@@ -212,6 +216,12 @@
if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
sb.append("!VIS ");
}
+ if ((flags & Context.BIND_NOT_PERCEPTIBLE) != 0) {
+ sb.append("!PRCP ");
+ }
+ if ((flags & Context.BIND_INCLUDE_CAPABILITIES) != 0) {
+ sb.append("CAPS ");
+ }
if (serviceDead) {
sb.append("DEAD ");
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 7abfcea..5ade50a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1264,7 +1264,7 @@
cr.trackProcState(procState, mAdjSeq, now);
trackedProcState = true;
}
- } else if ((cr.flags & Context.BIND_ADJUST_BELOW_PERCEPTIBLE) != 0
+ } else if ((cr.flags & Context.BIND_NOT_PERCEPTIBLE) != 0
&& clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
&& adj > ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
newAdj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index d64a2c2..b399971 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -184,8 +184,8 @@
// is not entirely fatal but is generally a bad idea.
static final int BACKUP_APP_ADJ = 300;
- // This is a process bound by the system that's more important than services but not so
- // perceptible that it affects the user immediately if killed.
+ // This is a process bound by the system (or other app) that's more important than services but
+ // not so perceptible that it affects the user immediately if killed.
static final int PERCEPTIBLE_LOW_APP_ADJ = 250;
// This is a process only hosting components that are perceptible to the
@@ -279,7 +279,7 @@
// can't give it a different value for every possible kind of process.
private final int[] mOomAdj = new int[] {
FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
- BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_LMK_FIRST_ADJ
+ PERCEPTIBLE_LOW_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_LMK_FIRST_ADJ
};
// These are the low-end OOM level limits. This is appropriate for an
// HVGA or smaller phone with less than 512MB. Values are in KB.
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 4b2e21d..cb64245 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -564,7 +564,7 @@
if (KeepaliveInfo.STARTING == ki.mStartedState) {
if (SUCCESS == reason) {
// Keepalive successfully started.
- if (DBG) Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
+ Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
ki.mStartedState = KeepaliveInfo.STARTED;
try {
ki.mCallback.onStarted(slot);
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index f6735d9..077c405 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -232,6 +232,11 @@
title = r.getString(R.string.network_switch_metered, toTransport);
details = r.getString(R.string.network_switch_metered_detail, toTransport,
fromTransport);
+ } else if (notifyType == NotificationType.NO_INTERNET
+ || notifyType == NotificationType.PARTIAL_CONNECTIVITY) {
+ // NO_INTERNET and PARTIAL_CONNECTIVITY notification for non-WiFi networks
+ // are sent, but they are not implemented yet.
+ return;
} else {
Slog.wtf(TAG, "Unknown notification type " + notifyType + " on network transport "
+ getTransportName(transportType));
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 65dac8b..7da128f 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -251,7 +251,7 @@
try {
binding = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_NOT_VISIBLE | Context.BIND_ADJUST_BELOW_PERCEPTIBLE,
+ | Context.BIND_NOT_PERCEPTIBLE,
new UserHandle(job.getUserId()));
} catch (SecurityException e) {
// Some permission policy, for example INTERACT_ACROSS_USERS and
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 82b16de..495a2ed 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -42,10 +42,10 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import static android.content.Context.BIND_ADJUST_BELOW_PERCEPTIBLE;
import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_NOT_PERCEPTIBLE;
import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
@@ -7786,12 +7786,12 @@
@Override
protected int getBindFlags() {
- // Most of the same flags as the base, but also add BIND_ADJUST_BELOW_PERCEPTIBLE
+ // Most of the same flags as the base, but also add BIND_NOT_PERCEPTIBLE
// because too many 3P apps could be kept in memory as notification listeners and
// cause extreme memory pressure.
// TODO: Change the binding lifecycle of NotificationListeners to avoid this situation.
return BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE
- | BIND_ADJUST_BELOW_PERCEPTIBLE | BIND_ALLOW_WHITELIST_MANAGEMENT;
+ | BIND_NOT_PERCEPTIBLE | BIND_ALLOW_WHITELIST_MANAGEMENT;
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4d84048..9f5fc92 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -111,9 +111,6 @@
import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
-import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
-import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
-import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
import android.Manifest;
import android.annotation.IntDef;
@@ -294,6 +291,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.IntPair;
import com.android.internal.util.Preconditions;
import com.android.server.AttributeCache;
import com.android.server.DeviceIdleController;
@@ -1507,7 +1505,8 @@
final List<String> whitelistedRestrictedPermissions = ((args.installFlags
& PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0
&& parentRes.pkg != null)
- ? parentRes.pkg.requestedPermissions : null;
+ ? parentRes.pkg.requestedPermissions
+ : args.whitelistedRestrictedPermissions;
// Handle the parent package
handlePackagePostInstall(parentRes, grantPermissions,
@@ -19801,6 +19800,8 @@
return;
}
+ final String packageName = ps.pkg.packageName;
+
// These are flags that can change base on user actions.
final int userSettableMask = FLAG_PERMISSION_USER_SET
| FLAG_PERMISSION_USER_FIXED
@@ -19810,8 +19811,59 @@
final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
| FLAG_PERMISSION_POLICY_FIXED;
- boolean writeInstallPermissions = false;
- boolean writeRuntimePermissions = false;
+ // Delay and combine non-async permission callbacks
+ final boolean[] permissionRemoved = new boolean[1];
+ final ArraySet<Long> revokedPermissions = new ArraySet<>();
+ final SparseBooleanArray updatedUsers = new SparseBooleanArray();
+
+ PermissionCallback delayingPermCallback = new PermissionCallback() {
+ public void onGidsChanged(int appId, int userId) {
+ mPermissionCallback.onGidsChanged(appId, userId);
+ }
+
+ public void onPermissionChanged() {
+ mPermissionCallback.onPermissionChanged();
+ }
+
+ public void onPermissionGranted(int uid, int userId) {
+ mPermissionCallback.onPermissionGranted(uid, userId);
+ }
+
+ public void onInstallPermissionGranted() {
+ mPermissionCallback.onInstallPermissionGranted();
+ }
+
+ public void onPermissionRevoked(int uid, int userId) {
+ revokedPermissions.add(IntPair.of(uid, userId));
+
+ updatedUsers.put(userId, true);
+ }
+
+ public void onInstallPermissionRevoked() {
+ mPermissionCallback.onInstallPermissionRevoked();
+ }
+
+ public void onPermissionUpdated(int[] updatedUserIds, boolean sync) {
+ for (int userId : updatedUserIds) {
+ if (sync) {
+ updatedUsers.put(userId, true);
+ } else {
+ // Don't override sync=true by sync=false
+ if (!updatedUsers.get(userId)) {
+ updatedUsers.put(userId, false);
+ }
+ }
+ }
+ }
+
+ public void onPermissionRemoved() {
+ permissionRemoved[0] = true;
+ }
+
+ public void onInstallPermissionUpdated() {
+ mPermissionCallback.onInstallPermissionUpdated();
+ }
+ };
final int permissionCount = ps.pkg.requestedPermissions.size();
for (int i = 0; i < permissionCount; i++) {
@@ -19843,26 +19895,20 @@
}
}
- final PermissionsState permissionsState = ps.getPermissionsState();
-
- final int oldFlags = permissionsState.getPermissionFlags(permName, userId);
+ final int oldFlags = mPermissionManager.getPermissionFlags(permName, packageName,
+ Process.SYSTEM_UID, userId);
// Always clear the user settable flags.
- final boolean hasInstallState =
- permissionsState.getInstallPermissionState(permName) != null;
// If permission review is enabled and this is a legacy app, mark the
// permission as requiring a review as this is the initial state.
int flags = 0;
if (ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
}
- if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) {
- if (hasInstallState) {
- writeInstallPermissions = true;
- } else {
- writeRuntimePermissions = true;
- }
- }
+
+ mPermissionManager.updatePermissionFlags(permName, packageName,
+ userSettableMask, flags, Process.SYSTEM_UID, userId, false,
+ delayingPermCallback);
// Below is only runtime permission handling.
if (!bp.isRuntime()) {
@@ -19876,35 +19922,42 @@
// If this permission was granted by default, make sure it is.
if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0) {
- if (permissionsState.grantRuntimePermission(bp, userId)
- != PERMISSION_OPERATION_FAILURE) {
- writeRuntimePermissions = true;
- }
+ mPermissionManager.grantRuntimePermission(permName, packageName, false,
+ Process.SYSTEM_UID, userId, delayingPermCallback);
// If permission review is enabled the permissions for a legacy apps
// are represented as constantly granted runtime ones, so don't revoke.
} else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
// Otherwise, reset the permission.
- final int revokeResult = permissionsState.revokeRuntimePermission(bp, userId);
- switch (revokeResult) {
- case PERMISSION_OPERATION_SUCCESS:
- case PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
- writeRuntimePermissions = true;
- final int appId = ps.appId;
- mHandler.post(
- () -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
- } break;
- }
+ mPermissionManager.revokeRuntimePermission(permName, packageName, false, userId,
+ delayingPermCallback);
}
}
- // Synchronously write as we are taking permissions away.
- if (writeRuntimePermissions) {
- mSettings.writeRuntimePermissionsForUserLPr(userId, true);
+ // Execute delayed callbacks
+ if (permissionRemoved[0]) {
+ mPermissionCallback.onPermissionRemoved();
}
- // Synchronously write as we are taking permissions away.
- if (writeInstallPermissions) {
- mSettings.writeLPr();
+ // Slight variation on the code in mPermissionCallback.onPermissionRevoked() as we cannot
+ // kill uid while holding mPackages-lock
+ if (!revokedPermissions.isEmpty()) {
+ int numRevokedPermissions = revokedPermissions.size();
+ for (int i = 0; i < numRevokedPermissions; i++) {
+ int revocationUID = IntPair.first(revokedPermissions.valueAt(i));
+ int revocationUserId = IntPair.second(revokedPermissions.valueAt(i));
+
+ mOnPermissionChangeListeners.onPermissionsChanged(revocationUID);
+
+ // Kill app later as we are holding mPackages
+ mHandler.post(() -> killUid(UserHandle.getAppId(revocationUID), revocationUserId,
+ KILL_APP_REASON_PERMISSIONS_REVOKED));
+ }
+ }
+
+ int numUpdatedUsers = updatedUsers.size();
+ for (int i = 0; i < numUpdatedUsers; i++) {
+ mSettings.writeRuntimePermissionsForUserLPr(updatedUsers.keyAt(i),
+ updatedUsers.valueAt(i));
}
}
@@ -24334,8 +24387,12 @@
@Override
public boolean isPermissionsReviewRequired(String packageName, int userId) {
synchronized (mPackages) {
- return mPermissionManager.isPermissionsReviewRequired(
- mPackages.get(packageName), userId);
+ final PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ return false;
+ }
+
+ return mPermissionManager.isPermissionsReviewRequired(pkg, userId);
}
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 22eb9fd..f8c4f6b 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -143,12 +143,6 @@
CONTACTS_PERMISSIONS.add(Manifest.permission.GET_ACCOUNTS);
}
- private static final Set<String> LOCATION_PERMISSIONS = new ArraySet<>();
- static {
- LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
- LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
- }
-
private static final Set<String> ALWAYS_LOCATION_PERMISSIONS = new ArraySet<>();
static {
ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
@@ -453,7 +447,8 @@
// SetupWizard
grantPermissionsToSystemPackage(
getKnownPackage(PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId), userId,
- PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, LOCATION_PERMISSIONS, CAMERA_PERMISSIONS);
+ PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
+ CAMERA_PERMISSIONS);
// Camera
grantPermissionsToSystemPackage(
@@ -585,7 +580,7 @@
// Maps
grantPermissionsToSystemPackage(
getDefaultSystemHandlerActivityPackageForCategory(Intent.CATEGORY_APP_MAPS, userId),
- userId, LOCATION_PERMISSIONS);
+ userId, ALWAYS_LOCATION_PERMISSIONS);
// Gallery
grantPermissionsToSystemPackage(
@@ -609,14 +604,14 @@
}
}
grantPermissionsToPackage(browserPackage, userId, false /* ignoreSystemPackage */,
- true /*whitelistRestrictedPermissions*/, LOCATION_PERMISSIONS);
+ true /*whitelistRestrictedPermissions*/, ALWAYS_LOCATION_PERMISSIONS);
// Voice interaction
if (voiceInteractPackageNames != null) {
for (String voiceInteractPackageName : voiceInteractPackageNames) {
grantPermissionsToSystemPackage(voiceInteractPackageName, userId,
CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
- PHONE_PERMISSIONS, SMS_PERMISSIONS, LOCATION_PERMISSIONS);
+ PHONE_PERMISSIONS, SMS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
}
}
@@ -625,7 +620,7 @@
grantPermissionsToSystemPackage(
getDefaultSystemHandlerActivityPackage(
SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
- userId, MICROPHONE_PERMISSIONS, LOCATION_PERMISSIONS);
+ userId, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
}
// Voice recognition
@@ -667,7 +662,7 @@
.addCategory(Intent.CATEGORY_LAUNCHER_APP);
grantPermissionsToSystemPackage(
getDefaultSystemHandlerActivityPackage(homeIntent, userId), userId,
- LOCATION_PERMISSIONS);
+ ALWAYS_LOCATION_PERMISSIONS);
// Watches
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
@@ -676,18 +671,18 @@
String wearPackage = getDefaultSystemHandlerActivityPackageForCategory(
Intent.CATEGORY_HOME_MAIN, userId);
grantPermissionsToSystemPackage(wearPackage, userId,
- CONTACTS_PERMISSIONS, MICROPHONE_PERMISSIONS, LOCATION_PERMISSIONS);
+ CONTACTS_PERMISSIONS, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
grantSystemFixedPermissionsToSystemPackage(wearPackage, userId, PHONE_PERMISSIONS);
// Fitness tracking on watches
grantPermissionsToSystemPackage(
getDefaultSystemHandlerActivityPackage(ACTION_TRACK, userId), userId,
- SENSORS_PERMISSIONS, LOCATION_PERMISSIONS);
+ SENSORS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
}
// Print Spooler
grantSystemFixedPermissionsToSystemPackage(PrintManager.PRINT_SPOOLER_PACKAGE_NAME, userId,
- LOCATION_PERMISSIONS);
+ ALWAYS_LOCATION_PERMISSIONS);
// EmergencyInfo
grantSystemFixedPermissionsToSystemPackage(
@@ -725,7 +720,7 @@
if (!TextUtils.isEmpty(textClassifierPackageName)) {
grantPermissionsToSystemPackage(textClassifierPackageName, userId,
PHONE_PERMISSIONS, SMS_PERMISSIONS, CALENDAR_PERMISSIONS,
- LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
+ ALWAYS_LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
}
// Atthention Service
@@ -835,7 +830,7 @@
}
for (String packageName : packageNames) {
grantPermissionsToSystemPackage(packageName, userId,
- PHONE_PERMISSIONS, MICROPHONE_PERMISSIONS, LOCATION_PERMISSIONS,
+ PHONE_PERMISSIONS, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
CAMERA_PERMISSIONS, CONTACTS_PERMISSIONS);
}
}
@@ -850,7 +845,7 @@
// Grant these permissions as system-fixed, so that nobody can accidentally
// break cellular data.
grantSystemFixedPermissionsToSystemPackage(packageName, userId,
- PHONE_PERMISSIONS, LOCATION_PERMISSIONS);
+ PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
}
}
@@ -864,7 +859,7 @@
PackageInfo pkg = getSystemPackageInfo(packageName);
if (isSystemPackage(pkg) && doesPackageSupportRuntimePermissions(pkg)) {
revokeRuntimePermissions(packageName, PHONE_PERMISSIONS, true, userId);
- revokeRuntimePermissions(packageName, LOCATION_PERMISSIONS, true, userId);
+ revokeRuntimePermissions(packageName, ALWAYS_LOCATION_PERMISSIONS, true, userId);
}
}
}
@@ -889,7 +884,7 @@
public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
Log.i(TAG, "Granting permissions to default browser for user:" + userId);
- grantPermissionsToSystemPackage(packageName, userId, LOCATION_PERMISSIONS);
+ grantPermissionsToSystemPackage(packageName, userId, ALWAYS_LOCATION_PERMISSIONS);
}
private String getDefaultSystemHandlerActivityPackage(String intentAction, int userId) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 4edd9ef..a7da3ec 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1897,14 +1897,15 @@
return Boolean.TRUE == granted;
}
- private boolean isPermissionsReviewRequired(PackageParser.Package pkg, int userId) {
+ private boolean isPermissionsReviewRequired(@NonNull PackageParser.Package pkg,
+ @UserIdInt int userId) {
// Permission review applies only to apps not supporting the new permission model.
if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
return false;
}
// Legacy apps have the permission and get user consent on launch.
- if (pkg == null || pkg.mExtras == null) {
+ if (pkg.mExtras == null) {
return false;
}
final PackageSetting ps = (PackageSetting) pkg.mExtras;
@@ -2128,7 +2129,7 @@
}
if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext,
- pkg.applicationInfo, permName).canBeGranted()) {
+ pkg.applicationInfo, UserHandle.of(userId), permName).canBeGranted()) {
Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package "
+ packageName);
return;
@@ -2952,7 +2953,7 @@
PermissionManagerService.this.systemReady();
}
@Override
- public boolean isPermissionsReviewRequired(Package pkg, int userId) {
+ public boolean isPermissionsReviewRequired(@NonNull Package pkg, @UserIdInt int userId) {
return PermissionManagerService.this.isPermissionsReviewRequired(pkg, userId);
}
@Override
@@ -3126,6 +3127,27 @@
}
@Override
+ public @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtectionLevel(
+ @PermissionInfo.Protection int protectionLevel) {
+ ArrayList<PermissionInfo> matchingPermissions = new ArrayList<>();
+
+ synchronized (PermissionManagerService.this.mLock) {
+ int numTotalPermissions = mSettings.mPermissions.size();
+
+ for (int i = 0; i < numTotalPermissions; i++) {
+ BasePermission bp = mSettings.mPermissions.valueAt(i);
+
+ if (bp.perm != null && bp.perm.info != null
+ && bp.protectionLevel == protectionLevel) {
+ matchingPermissions.add(bp.perm.info);
+ }
+ }
+ }
+
+ return matchingPermissions;
+ }
+
+ @Override
public @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) {
return PermissionManagerService.this.backupRuntimePermissions(user);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 9fb71f4..e5e9598 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.PermissionInfoFlags;
import android.content.pm.PackageParser;
@@ -65,7 +66,8 @@
public abstract void systemReady();
- public abstract boolean isPermissionsReviewRequired(PackageParser.Package pkg, int userId);
+ public abstract boolean isPermissionsReviewRequired(@NonNull PackageParser.Package pkg,
+ @UserIdInt int userId);
public abstract void grantRuntimePermission(
@NonNull String permName, @NonNull String packageName, boolean overridePolicy,
@@ -193,4 +195,8 @@
/** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
+
+ /** Get all permission that have a certain protection level */
+ public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtectionLevel(
+ @PermissionInfo.Protection int protectionLevel);
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 6882afb..68feb4a 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -23,6 +23,7 @@
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import android.annotation.NonNull;
@@ -41,20 +42,27 @@
import android.content.pm.PermissionInfo;
import android.os.Build;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.permission.PermissionControllerManager;
-import android.permission.PermissionManagerInternal;
import android.provider.Telephony;
import android.telecom.TelecomManager;
+import android.util.ArraySet;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
@@ -76,6 +84,13 @@
@GuardedBy("mLock")
private final SparseBooleanArray mIsStarted = new SparseBooleanArray();
+ /**
+ * Whether an async {@link #synchronizePackagePermissionsAndAppOpsForUser} is currently
+ * scheduled for a package/user.
+ */
+ @GuardedBy("mLock")
+ private final ArraySet<Pair<String, Integer>> mIsPackageSyncsScheduled = new ArraySet<>();
+
public PermissionPolicyService(@NonNull Context context) {
super(context);
@@ -86,8 +101,10 @@
public void onStart() {
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
- final PermissionManagerInternal permManagerInternal = LocalServices.getService(
- PermissionManagerInternal.class);
+ final PermissionManagerServiceInternal permManagerInternal = LocalServices.getService(
+ PermissionManagerServiceInternal.class);
+ final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_OPS_SERVICE));
packageManagerInternal.getPackageList(new PackageListObserver() {
@Override
@@ -111,11 +128,62 @@
});
permManagerInternal.addOnRuntimePermissionStateChangedListener(
- (packageName, changedUserId) -> {
- if (isStarted(changedUserId)) {
- synchronizePackagePermissionsAndAppOpsForUser(packageName, changedUserId);
+ this::synchronizePackagePermissionsAndAppOpsAsyncForUser);
+
+ IAppOpsCallback appOpsListener = new IAppOpsCallback.Stub() {
+ public void opChanged(int op, int uid, String packageName) {
+ synchronizePackagePermissionsAndAppOpsAsyncForUser(packageName,
+ UserHandle.getUserId(uid));
+ }
+ };
+
+ final ArrayList<PermissionInfo> dangerousPerms =
+ permManagerInternal.getAllPermissionWithProtectionLevel(
+ PermissionInfo.PROTECTION_DANGEROUS);
+
+ try {
+ int numDangerousPerms = dangerousPerms.size();
+ for (int i = 0; i < numDangerousPerms; i++) {
+ PermissionInfo perm = dangerousPerms.get(i);
+
+ if (perm.isHardRestricted() || perm.backgroundPermission != null) {
+ appOpsService.startWatchingMode(AppOpsManager.permissionToOpCode(perm.name),
+ null, appOpsListener);
+ } else if (perm.isSoftRestricted()) {
+ appOpsService.startWatchingMode(AppOpsManager.permissionToOpCode(perm.name),
+ null, appOpsListener);
+
+ SoftRestrictedPermissionPolicy policy =
+ SoftRestrictedPermissionPolicy.forPermission(null, null, null,
+ perm.name);
+ if (policy.resolveAppOp() != OP_NONE) {
+ appOpsService.startWatchingMode(policy.resolveAppOp(), null,
+ appOpsListener);
}
- });
+ }
+ }
+ } catch (RemoteException doesNotHappen) {
+ Slog.wtf(LOG_TAG, "Cannot set up app-ops listener");
+ }
+ }
+
+ private void synchronizePackagePermissionsAndAppOpsAsyncForUser(@NonNull String packageName,
+ @UserIdInt int changedUserId) {
+ if (isStarted(changedUserId)) {
+ synchronized (mLock) {
+ if (mIsPackageSyncsScheduled.add(new Pair<>(packageName, changedUserId))) {
+ FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
+ PermissionPolicyService
+ ::synchronizePackagePermissionsAndAppOpsForUser,
+ this, packageName, changedUserId));
+ } else {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "sync for " + packageName + "/" + changedUserId
+ + " already scheduled");
+ }
+ }
+ }
+ }
}
@Override
@@ -230,10 +298,14 @@
*/
private void synchronizePackagePermissionsAndAppOpsForUser(@NonNull String packageName,
@UserIdInt int userId) {
+ synchronized (mLock) {
+ mIsPackageSyncsScheduled.remove(new Pair<>(packageName, userId));
+ }
+
if (DEBUG) {
Slog.v(LOG_TAG,
- "synchronizePackagePermissionsAndAppOpsForUser(" + packageName + ", " + userId
- + ")");
+ "synchronizePackagePermissionsAndAppOpsForUser(" + packageName + ", "
+ + userId + ")");
}
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
@@ -336,6 +408,16 @@
*/
private final @NonNull ArrayList<OpToUnrestrict> mOpsToForeground = new ArrayList<>();
+ /**
+ * All ops that need to be flipped to foreground if allow.
+ *
+ * Currently, only used by the foreground/background permissions logic.
+ *
+ * @see #syncPackages
+ */
+ private final @NonNull ArrayList<OpToUnrestrict> mOpsToForegroundIfAllow =
+ new ArrayList<>();
+
PermissionToOpSynchroniser(@NonNull Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
@@ -351,22 +433,27 @@
final int allowCount = mOpsToAllow.size();
for (int i = 0; i < allowCount; i++) {
final OpToUnrestrict op = mOpsToAllow.get(i);
- setUidModeAllowed(op.code, op.uid);
+ setUidModeAllowed(op.code, op.uid, op.packageName);
}
final int allowIfDefaultCount = mOpsToAllowIfDefault.size();
for (int i = 0; i < allowIfDefaultCount; i++) {
final OpToUnrestrict op = mOpsToAllowIfDefault.get(i);
setUidModeAllowedIfDefault(op.code, op.uid, op.packageName);
}
- final int foregroundCount = mOpsToForeground.size();
+ final int foregroundCount = mOpsToForegroundIfAllow.size();
for (int i = 0; i < foregroundCount; i++) {
+ final OpToUnrestrict op = mOpsToForegroundIfAllow.get(i);
+ setUidModeForegroundIfAllow(op.code, op.uid, op.packageName);
+ }
+ final int foregroundIfAllowCount = mOpsToForeground.size();
+ for (int i = 0; i < foregroundIfAllowCount; i++) {
final OpToUnrestrict op = mOpsToForeground.get(i);
- setUidModeForeground(op.code, op.uid);
+ setUidModeForeground(op.code, op.uid, op.packageName);
}
final int ignoreCount = mOpsToIgnore.size();
for (int i = 0; i < ignoreCount; i++) {
final OpToUnrestrict op = mOpsToIgnore.get(i);
- setUidModeIgnored(op.code, op.uid);
+ setUidModeIgnored(op.code, op.uid, op.packageName);
}
final int ignoreIfDefaultCount = mOpsToIgnoreIfDefault.size();
for (int i = 0; i < ignoreIfDefaultCount; i++) {
@@ -414,7 +501,7 @@
} else if (permissionInfo.isSoftRestricted()) {
final SoftRestrictedPermissionPolicy policy =
SoftRestrictedPermissionPolicy.forPermission(mContext, pkg.applicationInfo,
- permission);
+ mContext.getUser(), permission);
if (opCode != OP_NONE) {
if (policy.canBeGranted()) {
@@ -459,6 +546,24 @@
}
}
+ private boolean isBgPermRestricted(@NonNull String pkg, @NonNull String perm, int uid) {
+ try {
+ final PermissionInfo bgPermInfo = mPackageManager.getPermissionInfo(perm, 0);
+
+ if (bgPermInfo.isSoftRestricted()) {
+ Slog.wtf(LOG_TAG, "Support for soft restricted background permissions not "
+ + "implemented");
+ }
+
+ return bgPermInfo.isHardRestricted() && (mPackageManager.getPermissionFlags(
+ perm, pkg, UserHandle.getUserHandleForUid(uid))
+ & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ } catch (NameNotFoundException e) {
+ Slog.w(LOG_TAG, "Cannot read permission state of " + perm, e);
+ return false;
+ }
+ }
+
/**
* Add op that belong to a foreground permission for later processing in
* {@link #syncPackages()}.
@@ -481,26 +586,27 @@
final String pkgName = pkg.packageName;
final int uid = pkg.applicationInfo.uid;
- if (mPackageManager.checkPermission(permission, pkgName)
- == PackageManager.PERMISSION_GRANTED) {
- boolean isBgHardRestricted = false;
- try {
- final PermissionInfo bgPermInfo = mPackageManager.getPermissionInfo(
- bgPermissionName, 0);
+ // App does not support runtime permissions. Hence the state is encoded in the app-op.
+ // To not override unrecoverable state don't change app-op unless bg perm is reviewed.
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ // If the review is required for this permission, the grant state does not
+ // really matter. To have a stable state, don't change the app-op if review is still
+ // pending.
+ int flags = mPackageManager.getPermissionFlags(bgPermissionName,
+ pkg.packageName, UserHandle.getUserHandleForUid(uid));
- if (bgPermInfo.isSoftRestricted()) {
- Slog.wtf(LOG_TAG, "Support for soft restricted background permissions not "
- + "implemented");
- }
-
- isBgHardRestricted =
- bgPermInfo.isHardRestricted() && (mPackageManager.getPermissionFlags(
- bgPermissionName, pkgName, UserHandle.getUserHandleForUid(uid))
- & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
- } catch (NameNotFoundException e) {
- Slog.w(LOG_TAG, "Cannot read permission state of " + bgPermissionName, e);
+ if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0
+ && isBgPermRestricted(pkgName, bgPermissionName, uid)) {
+ mOpsToForegroundIfAllow.add(new OpToUnrestrict(uid, pkgName, opCode));
}
+ return;
+ }
+
+ if (mPackageManager.checkPermission(permission, pkgName)
+ == PackageManager.PERMISSION_GRANTED) {
+ final boolean isBgHardRestricted = isBgPermRestricted(pkgName, bgPermissionName,
+ uid);
final boolean isBgPermGranted = mPackageManager.checkPermission(bgPermissionName,
pkgName) == PackageManager.PERMISSION_GRANTED;
@@ -554,45 +660,49 @@
}
private void setUidModeAllowedIfDefault(int opCode, int uid, @NonNull String packageName) {
- setUidModeIfDefault(opCode, uid, AppOpsManager.MODE_ALLOWED, packageName);
+ setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_ALLOWED, packageName);
}
- private void setUidModeAllowed(int opCode, int uid) {
- mAppOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_ALLOWED);
+ private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
+ setUidMode(opCode, uid, MODE_ALLOWED, packageName);
}
- private void setUidModeForeground(int opCode, int uid) {
- mAppOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_FOREGROUND);
+ private void setUidModeForegroundIfAllow(int opCode, int uid, @NonNull String packageName) {
+ setUidModeIfMode(opCode, uid, MODE_ALLOWED, MODE_FOREGROUND, packageName);
+ }
+
+ private void setUidModeForeground(int opCode, int uid, @NonNull String packageName) {
+ setUidMode(opCode, uid, MODE_FOREGROUND, packageName);
}
private void setUidModeIgnoredIfDefault(int opCode, int uid, @NonNull String packageName) {
- setUidModeIfDefault(opCode, uid, AppOpsManager.MODE_IGNORED, packageName);
+ setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_IGNORED, packageName);
}
- private void setUidModeIgnored(int opCode, int uid) {
- mAppOpsManager.setUidMode(opCode, uid, MODE_IGNORED);
+ private void setUidModeIgnored(int opCode, int uid, @NonNull String packageName) {
+ setUidMode(opCode, uid, MODE_IGNORED, packageName);
}
- private void setUidModeIfDefault(int opCode, int uid, int mode,
+ private void setUidMode(int opCode, int uid, int mode,
@NonNull String packageName) {
- final int currentMode;
- try {
- currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
- .opToPublicName(opCode), uid, packageName);
- } catch (SecurityException e) {
- // This might happen if the app was uninstalled in between the add and sync step.
- // In this case the package name cannot be resolved inside appops service and hence
- // the uid does not match.
- Slog.w(LOG_TAG, "Cannot set mode of uid=" + uid + " op=" + opCode + " to " + mode,
- e);
- return;
- }
+ final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
+ .opToPublicName(opCode), uid, packageName);
- if (currentMode == MODE_DEFAULT) {
+ if (currentMode != mode) {
mAppOpsManager.setUidMode(opCode, uid, mode);
}
}
+ private void setUidModeIfMode(int opCode, int uid, int requiredModeBefore, int newMode,
+ @NonNull String packageName) {
+ final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
+ .opToPublicName(opCode), uid, packageName);
+
+ if (currentMode == requiredModeBefore) {
+ mAppOpsManager.setUidMode(opCode, uid, newMode);
+ }
+ }
+
private void setUidModeDefault(int opCode, int uid) {
mAppOpsManager.setUidMode(opCode, uid, MODE_DEFAULT);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7e706ad..da87b2f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5199,6 +5199,11 @@
awakenDreams();
}
+ if (!isUserSetupComplete()) {
+ Slog.i(TAG, "Not going home because user setup is in progress.");
+ return;
+ }
+
// Start dock.
Intent dock = createHomeDockIntent();
if (dock != null) {
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index d447617..1658833 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -29,10 +29,12 @@
import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Build;
+import android.os.UserHandle;
/**
* The behavior of soft restricted permissions is different for each permission. This class collects
@@ -74,26 +76,43 @@
* Get the policy for a soft restricted permission.
*
* @param context A context to use
- * @param appInfo The application the permission belongs to
+ * @param appInfo The application the permission belongs to. Can be {@code null}, but then
+ * only {@link #resolveAppOp} will work.
+ * @param user The user the app belongs to. Can be {@code null}, but then only
+ * {@link #resolveAppOp} will work.
* @param permission The name of the permission
*
* @return The policy for this permission
*/
public static @NonNull SoftRestrictedPermissionPolicy forPermission(@NonNull Context context,
- @NonNull ApplicationInfo appInfo, @NonNull String permission) {
+ @Nullable ApplicationInfo appInfo, @Nullable UserHandle user,
+ @NonNull String permission) {
switch (permission) {
// Storage uses a special app op to decide the mount state and supports soft restriction
// where the restricted state allows the permission but only for accessing the medial
// collections.
case READ_EXTERNAL_STORAGE:
case WRITE_EXTERNAL_STORAGE: {
- int flags = context.getPackageManager().getPermissionFlags(
- permission, appInfo.packageName, context.getUser());
- boolean applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
- boolean isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
- boolean hasRequestedLegacyExternalStorage =
- appInfo.hasRequestedLegacyExternalStorage();
- int targetSDK = appInfo.targetSdkVersion;
+ final int flags;
+ final boolean applyRestriction;
+ final boolean isWhiteListed;
+ final boolean hasRequestedLegacyExternalStorage;
+ final int targetSDK;
+
+ if (appInfo != null) {
+ flags = context.getPackageManager().getPermissionFlags(permission,
+ appInfo.packageName, user);
+ applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
+ hasRequestedLegacyExternalStorage = appInfo.hasRequestedLegacyExternalStorage();
+ targetSDK = appInfo.targetSdkVersion;
+ } else {
+ flags = 0;
+ applyRestriction = false;
+ isWhiteListed = false;
+ hasRequestedLegacyExternalStorage = false;
+ targetSDK = 0;
+ }
return new SoftRestrictedPermissionPolicy() {
@Override
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 73775b4..5fd2ab8 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -26,10 +26,7 @@
import static android.os.Process.SYSTEM_UID;
import android.Manifest.permission;
-import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.app.ContentProviderHolder;
-import android.app.IActivityManager;
import android.app.slice.ISliceManager;
import android.app.slice.SliceSpec;
import android.app.usage.UsageStatsManagerInternal;
@@ -391,7 +388,7 @@
}
protected int checkAccess(String pkg, Uri uri, int uid, int pid) {
- return checkSlicePermission(uri, null, pkg, uid, pid, null);
+ return checkSlicePermission(uri, null, pkg, pid, uid, null);
}
private String getProviderPkg(Uri uri, int user) {
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 63439d5..0e2d1db 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1983,10 +1983,9 @@
e.writeString(permName);
e.writeInt(pkg.applicationInfo.uid);
e.writeString(pkg.packageName);
- e.writeInt(permissionFlags);
-
e.writeBoolean((pkg.requestedPermissionsFlags[permNum]
& REQUESTED_PERMISSION_GRANTED) != 0);
+ e.writeInt(permissionFlags);
pulledData.add(e);
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index feef5e2..bc0f747 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -544,14 +544,30 @@
// If we have an active transition that's waiting on a certain activity that will be
// invisible now, we'll never get onWindowsDrawn, so abort the transition if necessary.
- if (info != null && !hasVisibleNonFinishingActivity(t)) {
- if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible"
- + " activity=" + r);
- logAppTransitionCancel(info);
- mWindowingModeTransitionInfo.remove(r.getWindowingMode());
- if (mWindowingModeTransitionInfo.size() == 0) {
- reset(true /* abort */, info, "notifyVisibilityChanged to invisible");
- }
+
+ // We have no active transitions.
+ if (info == null) {
+ return;
+ }
+
+ // The notified activity whose visibility changed is no longer the launched activity.
+ // We can still wait to get onWindowsDrawn.
+ if (info.launchedActivity != r) {
+ return;
+ }
+
+ // Check if there is any activity in the task that is visible and not finishing. If the
+ // launched activity finished before it is drawn and if there is another activity in
+ // the task then that activity will be draw on screen.
+ if (hasVisibleNonFinishingActivity(t)) {
+ return;
+ }
+
+ if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible activity=" + r);
+ logAppTransitionCancel(info);
+ mWindowingModeTransitionInfo.remove(r.getWindowingMode());
+ if (mWindowingModeTransitionInfo.size() == 0) {
+ reset(true /* abort */, info, "notifyVisibilityChanged to invisible");
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1344727..0faea61 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2039,6 +2039,13 @@
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
WindowVisibilityItem.obtain(true /* showWindow */));
makeActiveIfNeeded(null /* activeActivity*/);
+ if (isState(STOPPING, STOPPED) && isFocusable()) {
+ // #shouldMakeActive() only evaluates the topmost activities in task, so
+ // activities that are not the topmost in task are not being resumed or paused.
+ // For activities that are still in STOPPING or STOPPED state, updates the state
+ // to PAUSE at least when making it visible.
+ setState(PAUSED, "makeClientVisible");
+ }
} catch (Exception e) {
Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index ddd5c0a..e6a83de 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -87,6 +87,7 @@
import android.content.res.ResourceId;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -258,6 +259,8 @@
private final boolean mGridLayoutRecentsEnabled;
private final boolean mLowRamRecentsEnabled;
+ private final int mDefaultWindowAnimationStyleResId;
+
private RemoteAnimationController mRemoteAnimationController;
final Handler mHandler;
@@ -305,6 +308,12 @@
* mContext.getResources().getDisplayMetrics().density);
mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
mLowRamRecentsEnabled = ActivityManager.isLowRamDeviceStatic();
+
+ final TypedArray windowStyle = mContext.getTheme().obtainStyledAttributes(
+ com.android.internal.R.styleable.Window);
+ mDefaultWindowAnimationStyleResId = windowStyle.getResourceId(
+ com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
+ windowStyle.recycle();
}
boolean isTransitionSet() {
@@ -523,6 +532,25 @@
return redoLayout;
}
+ @VisibleForTesting
+ int getDefaultWindowAnimationStyleResId() {
+ return mDefaultWindowAnimationStyleResId;
+ }
+
+ /** Returns window animation style ID from {@link LayoutParams} or from system in some cases */
+ @VisibleForTesting
+ int getAnimationStyleResId(@NonNull LayoutParams lp) {
+ int resId = lp.windowAnimations;
+ if (lp.type == LayoutParams.TYPE_APPLICATION_STARTING) {
+ // Note that we don't want application to customize starting window animation.
+ // Since this window is specific for displaying while app starting,
+ // application should not change its animation directly.
+ // In this case, it will use system resource to get default animation.
+ resId = mDefaultWindowAnimationStyleResId;
+ }
+ return resId;
+ }
+
private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
+ (lp != null ? lp.packageName : null)
@@ -532,7 +560,7 @@
// application resources. It is nice to avoid loading application
// resources if we can.
String packageName = lp.packageName != null ? lp.packageName : "android";
- int resId = lp.windowAnimations;
+ int resId = getAnimationStyleResId(lp);
if ((resId&0xFF000000) == 0x01000000) {
packageName = "android";
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 9189279..be3b924 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -79,6 +79,7 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
+import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
@@ -540,6 +541,14 @@
// If the app was already visible, don't reset the waitingToShow state.
if (isHidden()) {
waitingToShow = true;
+
+ // Let's reset the draw state in order to prevent the starting window to be
+ // immediately dismissed when the app still has the surface.
+ forAllWindows(w -> {
+ if (w.mWinAnimator.mDrawState == HAS_DRAWN) {
+ w.mWinAnimator.resetDrawState();
+ }
+ }, true /* traverseTopToBottom */);
}
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index e0ab722..541a8bb 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -308,11 +308,13 @@
*/
@VisibleForTesting
void resetFreezeTaskListReorderingOnTimeout() {
- final ActivityStack focusedStack = mService.getTopDisplayFocusedStack();
- final TaskRecord topTask = focusedStack != null
- ? focusedStack.topTask()
- : null;
- resetFreezeTaskListReordering(topTask);
+ synchronized (mService.mGlobalLock) {
+ final ActivityStack focusedStack = mService.getTopDisplayFocusedStack();
+ final TaskRecord topTask = focusedStack != null
+ ? focusedStack.topTask()
+ : null;
+ resetFreezeTaskListReordering(topTask);
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 1815218..9fdc045 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -95,6 +95,7 @@
private final ArraySet<Task> mSkipClosingAppSnapshotTasks = new ArraySet<>();
private final ArraySet<Task> mTmpTasks = new ArraySet<>();
private final Handler mHandler = new Handler();
+ private final float mFullSnapshotScale;
private final Rect mTmpRect = new Rect();
@@ -124,6 +125,8 @@
PackageManager.FEATURE_EMBEDDED);
mIsRunningOnWear = mService.mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WATCH);
+ mFullSnapshotScale = mService.mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_fullTaskSnapshotScale);
}
void systemReady() {
@@ -287,7 +290,9 @@
}
final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
- final float scaleFraction = isLowRamDevice ? mPersister.getReducedScale() : 1f;
+ final float scaleFraction = isLowRamDevice
+ ? mPersister.getReducedScale()
+ : mFullSnapshotScale;
final WindowState mainWindow = appWindowToken.findMainWindow();
if (mainWindow == null) {
@@ -306,7 +311,8 @@
final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
return new TaskSnapshot(
appWindowToken.mActivityComponent, screenshotBuffer.getGraphicBuffer(),
- screenshotBuffer.getColorSpace(), appWindowToken.getConfiguration().orientation,
+ screenshotBuffer.getColorSpace(),
+ appWindowToken.getTask().getConfiguration().orientation,
getInsets(mainWindow), isLowRamDevice /* reduced */, scaleFraction /* scale */,
true /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task),
!appWindowToken.fillsParent() || isWindowTranslucent);
@@ -378,9 +384,10 @@
task.getTaskDescription().getBackgroundColor(), 255);
final LayoutParams attrs = mainWindow.getAttrs();
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
- attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription());
- final int width = mainWindow.getFrameLw().width();
- final int height = mainWindow.getFrameLw().height();
+ attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
+ mFullSnapshotScale);
+ final int width = (int) (task.getBounds().width() * mFullSnapshotScale);
+ final int height = (int) (task.getBounds().height() * mFullSnapshotScale);
final RenderNode node = RenderNode.create("TaskSnapshotController", null);
node.setLeftTopRightBottom(0, 0, width, height);
@@ -394,12 +401,13 @@
if (hwBitmap == null) {
return null;
}
+
// Note, the app theme snapshot is never translucent because we enforce a non-translucent
// color above
return new TaskSnapshot(topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(),
- hwBitmap.getColorSpace(), topChild.getConfiguration().orientation,
- mainWindow.getStableInsets(), ActivityManager.isLowRamDeviceStatic() /* reduced */,
- 1.0f /* scale */, false /* isRealSnapshot */, task.getWindowingMode(),
+ hwBitmap.getColorSpace(), topChild.getTask().getConfiguration().orientation,
+ getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* reduced */,
+ mFullSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(),
getSystemUiVisibility(task), false);
}
@@ -481,6 +489,7 @@
}
void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "mFullSnapshotScale=" + mFullSnapshotScale);
mCache.dump(pw, prefix);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index f7b8945..fcd97c1 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -87,14 +87,16 @@
+ bitmapFile.getPath());
return null;
}
- ComponentName topActivityComponent = ComponentName.unflattenFromString(
+ final ComponentName topActivityComponent = ComponentName.unflattenFromString(
proto.topActivityComponent);
+ // For legacy snapshots, restore the scale based on the reduced resolution state
+ final float legacyScale = reducedResolution ? mPersister.getReducedScale() : 1f;
+ final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale;
return new TaskSnapshot(topActivityComponent, buffer, bitmap.getColorSpace(),
proto.orientation,
new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
- reducedResolution, reducedResolution ? mPersister.getReducedScale() : 1f,
- proto.isRealSnapshot, proto.windowingMode, proto.systemUiVisibility,
- proto.isTranslucent);
+ reducedResolution, scale, proto.isRealSnapshot, proto.windowingMode,
+ proto.systemUiVisibility, proto.isTranslucent);
} catch (IOException e) {
Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
return null;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 0b63f48..72fc189 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -342,6 +342,7 @@
proto.systemUiVisibility = mSnapshot.getSystemUiVisibility();
proto.isTranslucent = mSnapshot.isTranslucent();
proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString();
+ proto.scale = mSnapshot.getScale();
final byte[] bytes = TaskSnapshotProto.toByteArray(proto);
final File file = getProtoFile(mTaskId, mUserId);
final AtomicFile atomicFile = new AtomicFile(file);
@@ -369,12 +370,13 @@
}
final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */);
- final File reducedFile = getReducedResolutionBitmapFile(mTaskId, mUserId);
final Bitmap reduced = mSnapshot.isReducedResolution()
? swBitmap
: Bitmap.createScaledBitmap(swBitmap,
(int) (bitmap.getWidth() * mReducedScale),
(int) (bitmap.getHeight() * mReducedScale), true /* filter */);
+
+ final File reducedFile = getReducedResolutionBitmapFile(mTaskId, mUserId);
try {
FileOutputStream reducedFos = new FileOutputStream(reducedFile);
reduced.compress(JPEG, QUALITY, reducedFos);
@@ -383,6 +385,7 @@
Slog.e(TAG, "Unable to open " + reducedFile +" for persisting.", e);
return false;
}
+ reduced.recycle();
// For snapshots with reduced resolution, do not create or save full sized bitmaps
if (mSnapshot.isReducedResolution()) {
@@ -399,7 +402,6 @@
Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
return false;
}
- reduced.recycle();
swBitmap.recycle();
return true;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 5d99db5..a7760a86 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -248,7 +248,7 @@
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
mTaskBounds = taskBounds;
mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
- windowPrivateFlags, sysUiVis, taskDescription);
+ windowPrivateFlags, sysUiVis, taskDescription, 1f);
mStatusBarColor = taskDescription.getStatusBarColor();
mOrientationOnCreation = currentOrientation;
}
@@ -488,12 +488,14 @@
private final int mWindowFlags;
private final int mWindowPrivateFlags;
private final int mSysUiVis;
+ private final float mScale;
- SystemBarBackgroundPainter( int windowFlags, int windowPrivateFlags, int sysUiVis,
- TaskDescription taskDescription) {
+ SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis,
+ TaskDescription taskDescription, float scale) {
mWindowFlags = windowFlags;
mWindowPrivateFlags = windowPrivateFlags;
mSysUiVis = sysUiVis;
+ mScale = scale;
final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
final int semiTransparent = context.getColor(
R.color.system_bar_background_semi_transparent);
@@ -521,7 +523,7 @@
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
mSysUiVis, mStatusBarColor, mWindowFlags, forceBarBackground)) {
- return getColorViewTopInset(mStableInsets.top, mContentInsets.top);
+ return (int) (getColorViewTopInset(mStableInsets.top, mContentInsets.top) * mScale);
} else {
return 0;
}
@@ -544,8 +546,8 @@
int statusBarHeight) {
if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
&& (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
- final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right,
- mContentInsets.right);
+ final int rightInset = (int) (DecorView.getColorViewRightInset(mStableInsets.right,
+ mContentInsets.right) * mScale);
final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
}
@@ -555,7 +557,7 @@
void drawNavigationBarBackground(Canvas c) {
final Rect navigationBarRect = new Rect();
getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
- navigationBarRect);
+ navigationBarRect, mScale);
final boolean visible = isNavigationBarColorViewVisible();
if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
c.drawRect(navigationBarRect, mNavigationBarPaint);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ce8720a..57fa2ed 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7626,22 +7626,30 @@
@Override
public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) {
- boolean shouldWaitForAnimToComplete = false;
+ boolean isDown;
+ boolean isUp;
+
if (ev instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent) ev;
- shouldWaitForAnimToComplete = keyEvent.getSource() == InputDevice.SOURCE_MOUSE
- || keyEvent.getAction() == KeyEvent.ACTION_DOWN;
- } else if (ev instanceof MotionEvent) {
+ isDown = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
+ isUp = keyEvent.getAction() == KeyEvent.ACTION_UP;
+ } else {
MotionEvent motionEvent = (MotionEvent) ev;
- shouldWaitForAnimToComplete = motionEvent.getSource() == InputDevice.SOURCE_MOUSE
- || motionEvent.getAction() == MotionEvent.ACTION_DOWN;
+ isDown = motionEvent.getAction() == MotionEvent.ACTION_DOWN;
+ isUp = motionEvent.getAction() == MotionEvent.ACTION_UP;
}
- if (shouldWaitForAnimToComplete) {
+ // For ACTION_DOWN, syncInputTransactions before injecting input.
+ // For ACTION_UP, sync after injecting.
+ if (isDown) {
syncInputTransactions();
}
-
- return LocalServices.getService(InputManagerInternal.class).injectInputEvent(ev, mode);
+ final boolean result =
+ LocalServices.getService(InputManagerInternal.class).injectInputEvent(ev, mode);
+ if (isUp) {
+ syncInputTransactions();
+ }
+ return result;
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 10a63ee9..2c0a733 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -20,6 +20,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_NONE;
@@ -1294,6 +1295,7 @@
if (mWin.mSkipEnterAnimationForSeamlessReplacement) {
return;
}
+
final int transit;
if (mEnterAnimationPending) {
mEnterAnimationPending = false;
@@ -1301,7 +1303,13 @@
} else {
transit = WindowManagerPolicy.TRANSIT_SHOW;
}
- applyAnimationLocked(transit, true);
+
+ // We don't apply animation for application main window here since this window type
+ // should be controlled by AppWindowToken in general.
+ if (mAttrType != TYPE_BASE_APPLICATION) {
+ applyAnimationLocked(transit, true);
+ }
+
if (mService.mAccessibilityController != null) {
mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
}
@@ -1365,6 +1373,7 @@
+ " anim=" + anim + " attr=0x" + Integer.toHexString(attr)
+ " a=" + a
+ " transit=" + transit
+ + " type=" + mAttrType
+ " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
if (a != null) {
if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ba59cdb..8b61208 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6443,7 +6443,7 @@
.setAdmin(admin)
.setStrings(vpnPackage)
.setBoolean(lockdown)
- .setInt(/* number of vpn packages */ 0)
+ .setInt(lockdownWhitelist != null ? lockdownWhitelist.size() : 0)
.write();
} finally {
mInjector.binderRestoreCallingIdentity(token);
diff --git a/services/net/java/android/net/ipmemorystore/NetworkAttributes.java b/services/net/java/android/net/ipmemorystore/NetworkAttributes.java
index e769769..818515a 100644
--- a/services/net/java/android/net/ipmemorystore/NetworkAttributes.java
+++ b/services/net/java/android/net/ipmemorystore/NetworkAttributes.java
@@ -127,6 +127,7 @@
@Nullable
private static InetAddress getByAddressOrNull(@Nullable final byte[] address) {
+ if (null == address) return null;
try {
return InetAddress.getByAddress(address);
} catch (UnknownHostException e) {
@@ -227,7 +228,9 @@
}
/**
- * Set the lease expiry timestamp of assigned v4 address.
+ * Set the lease expiry timestamp of assigned v4 address. Long.MAX_VALUE is used
+ * to represent "infinite lease".
+ *
* @param assignedV4AddressExpiry The lease expiry timestamp of assigned v4 address.
* @return This builder.
*/
diff --git a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
index ca6f302..395ad98 100644
--- a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
+++ b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
@@ -40,8 +40,8 @@
// NonNull, but still don't crash the system server if null
if (null != listener) {
listener.onNetworkAttributesRetrieved(
- new Status(statusParcelable), l2Key,
- new NetworkAttributes(networkAttributesParcelable));
+ new Status(statusParcelable), l2Key, null == networkAttributesParcelable
+ ? null : new NetworkAttributes(networkAttributesParcelable));
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index a1d48cc..14b71ec 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -49,7 +49,7 @@
}
@Before
- public void setup() {
+ public final void setup() {
MockitoAnnotations.initMocks(this);
// Share classloader to allow package access.
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index 4c2822a..f5002ac 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -68,7 +68,7 @@
private IBinder mToken = new Binder();
@Before
- public void setup() {
+ public void setUp() {
mSliceService = mock(SliceManagerService.class);
when(mSliceService.getContext()).thenReturn(mContext);
when(mSliceService.getLock()).thenReturn(new Object());
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
index d942c5a..4958daf 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
@@ -47,7 +47,7 @@
private SliceFullAccessList mAccessList;
@Before
- public void setup() {
+ public void setUp() {
mAccessList = new SliceFullAccessList(mContext);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 7182f0f..a443695 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -71,7 +71,7 @@
private TestableContext mContextSpy;
@Before
- public void setup() {
+ public void setUp() {
LocalServices.addService(UsageStatsManagerInternal.class,
mock(UsageStatsManagerInternal.class));
mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
new file mode 100644
index 0000000..fc24f5e
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package com.android.server.policy;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.app.ActivityManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test class for {@link PhoneWindowManager}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:PhoneWindowManagerTests
+ */
+@SmallTest
+public class PhoneWindowManagerTests {
+
+ PhoneWindowManager mPhoneWindowManager;
+
+ @Before
+ public void setUp() {
+ mPhoneWindowManager = spy(new PhoneWindowManager());
+ spyOn(ActivityManager.getService());
+ }
+
+ @After
+ public void tearDown() {
+ reset(ActivityManager.getService());
+ }
+
+ @Test
+ public void testShouldNotStartDockOrHomeWhenSetup() throws Exception {
+ mockStartDockOrHome();
+ doReturn(false).when(mPhoneWindowManager).isUserSetupComplete();
+
+ mPhoneWindowManager.startDockOrHome(
+ 0 /* displayId */, false /* fromHomeKey */, false /* awakenFromDreams */);
+
+ verify(mPhoneWindowManager, never()).createHomeDockIntent();
+ }
+
+ @Test
+ public void testShouldStartDockOrHomeAfterSetup() throws Exception {
+ mockStartDockOrHome();
+ doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
+
+ mPhoneWindowManager.startDockOrHome(
+ 0 /* displayId */, false /* fromHomeKey */, false /* awakenFromDreams */);
+
+ verify(mPhoneWindowManager).createHomeDockIntent();
+ }
+
+ private void mockStartDockOrHome() throws Exception {
+ doNothing().when(ActivityManager.getService()).stopAppSwitches();
+ ActivityTaskManagerInternal mMockActivityTaskManagerInternal =
+ mock(ActivityTaskManagerInternal.class);
+ when(mMockActivityTaskManagerInternal.startHomeOnDisplay(
+ anyInt(), anyString(), anyInt(), anyBoolean(), anyBoolean())).thenReturn(false);
+ mPhoneWindowManager.mActivityTaskManagerInternal = mMockActivityTaskManagerInternal;
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 11a177a..8fbb7f5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -31,6 +31,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
@@ -56,7 +57,6 @@
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
import android.util.MutableBoolean;
@@ -163,12 +163,10 @@
// Make sure the state does not change if we are not the current top activity.
mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind");
- // Make sure that the state does not change when we have an activity becoming translucent
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
mStack.mTranslucentActivityWaiting = topActivity;
mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
-
- assertTrue(mActivity.isState(STOPPED));
+ assertTrue(mActivity.isState(PAUSED));
}
private void ensureActivityConfiguration() {
@@ -439,6 +437,15 @@
}
@Test
+ public void testShouldPauseWhenMakeClientVisible() {
+ ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+ topActivity.changeWindowTranslucency(false);
+ mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
+ mActivity.makeClientVisible();
+ assertEquals(PAUSED, mActivity.getState());
+ }
+
+ @Test
public void testSizeCompatMode_FixedAspectRatioBoundsWithDecor() {
setupDisplayContentForCompatDisplayInsets();
final int decorHeight = 200; // e.g. The device has cutout.
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 5a72a58..f602418 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
@@ -45,6 +46,7 @@
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
@@ -224,6 +226,21 @@
assertTrue(runner.mCancelled);
}
+ @Test
+ public void testGetAnimationStyleResId() {
+ // Verify getAnimationStyleResId will return as LayoutParams.windowAnimations when without
+ // specifying window type.
+ final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
+ attrs.windowAnimations = 0x12345678;
+ assertEquals(attrs.windowAnimations, mDc.mAppTransition.getAnimationStyleResId(attrs));
+
+ // Verify getAnimationStyleResId will return system resource Id when the window type is
+ // starting window.
+ attrs.type = TYPE_APPLICATION_STARTING;
+ assertEquals(mDc.mAppTransition.getDefaultWindowAnimationStyleResId(),
+ mDc.mAppTransition.getAnimationStyleResId(attrs));
+ }
+
private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
boolean mCancelled = false;
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index b0eafee..f958867 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -145,7 +145,10 @@
@Test
public void testLowResolutionPersistAndLoadSnapshot() {
- TaskSnapshot a = createSnapshot(0.5f /* reducedResolution */);
+ TaskSnapshot a = new TaskSnapshotBuilder()
+ .setScale(0.5f)
+ .setReducedResolution(true)
+ .build();
assertTrue(a.isReducedResolution());
mPersister.persistSnapshot(1 , mTestUserId, a);
mPersister.waitForQueueEmpty();
@@ -253,6 +256,27 @@
}
@Test
+ public void testScalePersistAndLoadSnapshot() {
+ TaskSnapshot a = new TaskSnapshotBuilder()
+ .setScale(0.25f)
+ .build();
+ TaskSnapshot b = new TaskSnapshotBuilder()
+ .setScale(0.75f)
+ .build();
+ assertEquals(0.25f, a.getScale(), 1E-5);
+ assertEquals(0.75f, b.getScale(), 1E-5);
+ mPersister.persistSnapshot(1, mTestUserId, a);
+ mPersister.persistSnapshot(2, mTestUserId, b);
+ mPersister.waitForQueueEmpty();
+ final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* reduced */);
+ final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */);
+ assertNotNull(snapshotA);
+ assertNotNull(snapshotB);
+ assertEquals(0.25f, snapshotA.getScale(), 1E-5);
+ assertEquals(0.75f, snapshotB.getScale(), 1E-5);
+ }
+
+ @Test
public void testRemoveObsoleteFiles() {
mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
mPersister.persistSnapshot(2, mTestUserId, createSnapshot());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index c3b0a67..e004cd3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -77,12 +77,7 @@
}
TaskSnapshot createSnapshot() {
- return createSnapshot(1f /* scale */);
- }
-
- TaskSnapshot createSnapshot(float scale) {
return new TaskSnapshotBuilder()
- .setScale(scale)
.build();
}
@@ -92,6 +87,7 @@
static class TaskSnapshotBuilder {
private float mScale = 1f;
+ private boolean mReducedResolution = false;
private boolean mIsRealSnapshot = true;
private boolean mIsTranslucent = false;
private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
@@ -102,6 +98,11 @@
return this;
}
+ TaskSnapshotBuilder setReducedResolution(boolean reducedResolution) {
+ mReducedResolution = reducedResolution;
+ return this;
+ }
+
TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) {
mIsRealSnapshot = isRealSnapshot;
return this;
@@ -130,7 +131,7 @@
buffer.unlockCanvasAndPost(c);
return new TaskSnapshot(new ComponentName("", ""), buffer,
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, TEST_INSETS,
- mScale < 1f /* reducedResolution */, mScale, mIsRealSnapshot,
+ mReducedResolution, mScale, mIsRealSnapshot,
mWindowingMode, mSystemUiVisibility, mIsTranslucent);
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 65db458..a10454e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -791,6 +791,14 @@
public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET;
/**
+ * IMSI (International Mobile Subscriber Identity).
+ * <P>Type: TEXT </P>
+ * @hide
+ */
+ //TODO: add @SystemApi
+ public static final String IMSI = "imsi";
+
+ /**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
*
@@ -3070,18 +3078,10 @@
}
/**
- * Returns whether the subscription is enabled or not. This is different from activated
- * or deactivated for two aspects. 1) For when user disables a physical subscription, we
- * actually disable the modem because we can't switch off the subscription. 2) For eSIM,
- * user may enable one subscription but the system may activate another temporarily. In this
- * case, user enabled one is different from current active one.
-
- * @param subscriptionId The subscription it asks about.
- * @return whether it's enabled or not. {@code true} if user set this subscription enabled
- * earlier, or user never set subscription enable / disable on this slot explicitly, and
- * this subscription is currently active. Otherwise, it returns {@code false}.
- *
+ * DO NOT USE.
+ * This API is designed for features that are not finished at this point. Do not call this API.
* @hide
+ * TODO b/135547512: further clean up
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3099,14 +3099,10 @@
}
/**
- * Get which subscription is enabled on this slot. See {@link #isSubscriptionEnabled(int)}
- * for more details.
- *
- * @param slotIndex which slot it asks about.
- * @return which subscription is enabled on this slot. If there's no enabled subscription
- * in this slot, it will return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
- *
+ * DO NOT USE.
+ * This API is designed for features that are not finished at this point. Do not call this API.
* @hide
+ * TODO b/135547512: further clean up
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b7b511e..39ad0d0 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1605,7 +1605,7 @@
* higher, then a SecurityException is thrown.</li>
* </ul>
*
- * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
+ * @deprecated Use {@link #getImei} which returns IMEI for GSM or {@link #getMeid} which returns
* MEID for CDMA.
*/
@Deprecated
@@ -1648,7 +1648,7 @@
*
* @param slotIndex of which deviceID is returned
*
- * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
+ * @deprecated Use {@link #getImei} which returns IMEI for GSM or {@link #getMeid} which returns
* MEID for CDMA.
*/
@Deprecated
@@ -1672,23 +1672,8 @@
* Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
* available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
- * managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
- *
- * <p>If the calling app does not meet one of these requirements then this method will behave
- * as follows:
- *
- * <ul>
- * <li>If the calling app's target SDK is API level 28 or lower and the app has the
- * READ_PHONE_STATE permission then null is returned.</li>
- * <li>If the calling app's target SDK is API level 28 or lower and the app does not have
- * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
- * higher, then a SecurityException is thrown.</li>
- * </ul>
+ * See {@link #getImei(int)} for details on the required permissions and behavior
+ * when the caller does not hold sufficient permissions.
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -1700,12 +1685,17 @@
* Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
* available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
- * managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>This API requires one of the following:
+ * <ul>
+ * <li>The caller holds the READ_PRIVILEGED_PHONE_STATE permission.</li>
+ * <li>If the caller is the device or profile owner, the caller holds the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission.</li>
+ * <li>The caller has carrier privileges (see {@link #hasCarrierPrivileges()}.</li>
+ * <li>The caller is the default SMS app for the device.</li>
+ * </ul>
+ * <p>The profile owner is an app that owns a managed profile on the device; for more details
+ * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Access by profile owners is deprecated and will be removed in a future release.
*
* <p>If the calling app does not meet one of these requirements then this method will behave
* as follows:
diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
index 814e06e..8ea226d 100644
--- a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
+++ b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
@@ -78,7 +78,6 @@
assertRunWithException(arrayOf("5"))
// Check resource with invalid slots value.
- assertRunWithException(arrayOf("2,2"))
assertRunWithException(arrayOf("3,-1"))
// Check resource with invalid transport type.