Merge "Consider lock icon when determining minimum top padding." into qt-dev
diff --git a/api/test-current.txt b/api/test-current.txt
index aacf2c1..95f4a2e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3262,6 +3262,7 @@
ctor public AutofillId(@NonNull android.view.autofill.AutofillId, int);
ctor public AutofillId(int, int);
ctor public AutofillId(@NonNull android.view.autofill.AutofillId, long, int);
+ method public boolean equalsIgnoreSession(@Nullable android.view.autofill.AutofillId);
}
public final class AutofillManager {
@@ -3390,6 +3391,7 @@
package android.view.inputmethod {
public final class InputMethodManager {
+ method public int getDisplayId();
method public boolean isInputMethodPickerShown();
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4abf924..2914f6c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -126,7 +126,6 @@
import android.view.autofill.AutofillManager.AutofillClient;
import android.view.autofill.AutofillPopupWindow;
import android.view.autofill.IAutofillWindowPresenter;
-import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
import android.widget.AdapterView;
@@ -840,7 +839,7 @@
/** The autofill manager. Always access via {@link #getAutofillManager()}. */
@Nullable private AutofillManager mAutofillManager;
- /** The content capture manager. Always access via {@link #getContentCaptureManager()}. */
+ /** The content capture manager. Access via {@link #getContentCaptureManager()}. */
@Nullable private ContentCaptureManager mContentCaptureManager;
private final ArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
@@ -1092,12 +1091,11 @@
case CONTENT_CAPTURE_START:
//TODO(b/111276913): decide whether the InteractionSessionId should be
// saved / restored in the activity bundle - probably not
- int flags = 0;
- if ((getWindow().getAttributes().flags
- & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
- flags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
+ final Window window = getWindow();
+ if (window != null) {
+ cm.updateWindowAttributes(window.getAttributes());
}
- cm.onActivityCreated(mToken, getComponentName(), flags);
+ cm.onActivityCreated(mToken, getComponentName());
break;
case CONTENT_CAPTURE_RESUME:
cm.onActivityResumed();
@@ -3785,6 +3783,9 @@
View decor = mDecor;
if (decor != null && decor.getParent() != null) {
getWindowManager().updateViewLayout(decor, params);
+ if (mContentCaptureManager != null) {
+ mContentCaptureManager.updateWindowAttributes(params);
+ }
}
}
}
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index b64b2dc..864af8c 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -28,6 +28,7 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Insets;
+import android.graphics.Matrix;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
@@ -51,6 +52,7 @@
import android.view.ViewParent;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.view.inputmethod.InputMethodManager;
import dalvik.system.CloseGuard;
@@ -320,6 +322,14 @@
updateLocationAndTapExcludeRegion();
}
+ private void clearActivityViewGeometryForIme() {
+ if (mVirtualDisplay == null) {
+ return;
+ }
+ final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
+ mContext.getSystemService(InputMethodManager.class).reportActivityView(displayId, null);
+ }
+
@Override
public void onLayout(boolean changed, int l, int t, int r, int b) {
mSurfaceView.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */);
@@ -347,8 +357,17 @@
if (x != mLocationInWindow[0] || y != mLocationInWindow[1]) {
x = mLocationInWindow[0];
y = mLocationInWindow[1];
+ final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
WindowManagerGlobal.getWindowSession().updateDisplayContentLocation(
- getWindow(), x, y, mVirtualDisplay.getDisplay().getDisplayId());
+ getWindow(), x, y, displayId);
+
+ // Also report this geometry information to InputMethodManagerService.
+ // TODO(b/115693908): Unify this logic into the above WMS-based one.
+ final Matrix matrix = new Matrix();
+ matrix.set(getMatrix());
+ matrix.postTranslate(x, y);
+ mContext.getSystemService(InputMethodManager.class)
+ .reportActivityView(displayId, matrix);
}
updateTapExcludeRegion(x, y);
} catch (RemoteException e) {
@@ -408,6 +427,7 @@
if (mVirtualDisplay != null) {
mVirtualDisplay.setDisplayState(false);
}
+ clearActivityViewGeometryForIme();
cleanTapExcludeRegion();
}
}
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 97b9176..a4a97c4 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -19,6 +19,7 @@
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager.TaskSnapshot;
import android.content.ComponentName;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -155,6 +156,11 @@
@Override
@UnsupportedAppUsage
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) throws RemoteException {
+ if (Binder.getCallingPid() != android.os.Process.myPid()
+ && snapshot != null && snapshot.getSnapshot() != null) {
+ // Preemptively clear any reference to the buffer
+ snapshot.getSnapshot().destroy();
+ }
}
@Override
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 0d5a763..3fca311 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -699,6 +699,7 @@
static final int AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS = 0x100;
static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS = 0x200;
static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH = 0x400;
+ static final int AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID = 0x800;
int mFlags;
int mAutofillFlags;
@@ -754,6 +755,9 @@
} else {
mAutofillId = new AutofillId(autofillViewId);
}
+ if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) {
+ mAutofillId.setSessionId(in.readInt());
+ }
}
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) {
mAutofillType = in.readInt();
@@ -899,6 +903,9 @@
if (mAutofillId.isVirtualInt()) {
autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID;
}
+ if (mAutofillId.hasSession()) {
+ autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID;
+ }
}
if (mAutofillValue != null) {
autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE;
@@ -965,7 +972,9 @@
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID) != 0) {
out.writeInt(mAutofillId.getVirtualChildIntId());
}
- // TODO(b/113593220): write session id as well
+ if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) {
+ out.writeInt(mAutofillId.getSessionId());
+ }
}
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) {
out.writeInt(mAutofillType);
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 442b239..1fe1b10 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -22,23 +22,16 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
-
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
@@ -209,101 +202,32 @@
@UnsupportedAppUsage
public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
- private Context mContext;
- private ServiceListener mServiceListener;
- private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
- @GuardedBy("mServiceLock")
- private IBluetoothA2dp mService;
private BluetoothAdapter mAdapter;
-
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- try {
- mServiceLock.writeLock().lock();
- if (mService != null) {
- mService = null;
- mContext.unbindService(mConnection);
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- } finally {
- mServiceLock.writeLock().unlock();
- }
- } else {
- try {
- mServiceLock.readLock().lock();
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- } finally {
- mServiceLock.readLock().unlock();
- }
- }
+ private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp",
+ IBluetoothA2dp.class.getName()) {
+ @Override
+ public IBluetoothA2dp getServiceInterface(IBinder service) {
+ return IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
}
- };
+ };
/**
* Create a BluetoothA2dp proxy object for interacting with the local
* Bluetooth A2DP service.
*/
- /*package*/ BluetoothA2dp(Context context, ServiceListener l) {
- mContext = context;
- mServiceListener = l;
+ /*package*/ BluetoothA2dp(Context context, ServiceListener listener) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
-
- doBind();
- }
-
- boolean doBind() {
- Intent intent = new Intent(IBluetoothA2dp.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
- return false;
- }
- return true;
+ mProfileConnector.connect(context, listener);
}
@UnsupportedAppUsage
/*package*/ void close() {
- mServiceListener = null;
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
+ mProfileConnector.disconnect();
+ }
- try {
- mServiceLock.writeLock().lock();
- if (mService != null) {
- mService = null;
- mContext.unbindService(mConnection);
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- } finally {
- mServiceLock.writeLock().unlock();
- }
+ private IBluetoothA2dp getService() {
+ return mProfileConnector.getService();
}
@Override
@@ -333,17 +257,15 @@
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled() && isValidDevice(device)) {
- return mService.connect(device);
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ return service.connect(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -376,17 +298,15 @@
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled() && isValidDevice(device)) {
- return mService.disconnect(device);
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ return service.disconnect(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -397,17 +317,15 @@
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- return mService.getConnectedDevices();
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()) {
+ return service.getConnectedDevices();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -418,17 +336,15 @@
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- return mService.getDevicesMatchingConnectionStates(states);
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()) {
+ return service.getDevicesMatchingConnectionStates(states);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -439,18 +355,16 @@
public @BtProfileState int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()
&& isValidDevice(device)) {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -480,18 +394,16 @@
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()
&& ((device == null) || isValidDevice(device))) {
- return mService.setActiveDevice(device);
+ return service.setActiveDevice(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -511,17 +423,15 @@
public BluetoothDevice getActiveDevice() {
if (VDBG) log("getActiveDevice()");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- return mService.getActiveDevice();
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()) {
+ return service.getActiveDevice();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return null;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return null;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -543,22 +453,20 @@
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()
&& isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
- return mService.setPriority(device, priority);
+ return service.setPriority(device, priority);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -578,18 +486,16 @@
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()
&& isValidDevice(device)) {
- return mService.getPriority(device);
+ return service.getPriority(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.PRIORITY_OFF;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.PRIORITY_OFF;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -602,17 +508,15 @@
public boolean isAvrcpAbsoluteVolumeSupported() {
if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- return mService.isAvrcpAbsoluteVolumeSupported();
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()) {
+ return service.isAvrcpAbsoluteVolumeSupported();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
return false;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -625,15 +529,13 @@
public void setAvrcpAbsoluteVolume(int volume) {
if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- mService.setAvrcpAbsoluteVolume(volume);
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()) {
+ service.setAvrcpAbsoluteVolume(volume);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -646,18 +548,16 @@
*/
public boolean isA2dpPlaying(BluetoothDevice device) {
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()
&& isValidDevice(device)) {
- return mService.isA2dpPlaying(device);
+ return service.isA2dpPlaying(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -694,19 +594,17 @@
public @Nullable BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- return mService.getCodecStatus(device);
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()) {
+ return service.getCodecStatus(device);
}
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
}
return null;
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
return null;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -723,17 +621,15 @@
BluetoothCodecConfig codecConfig) {
if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- mService.setCodecConfigPreference(device, codecConfig);
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()) {
+ service.setCodecConfigPreference(device, codecConfig);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return;
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
return;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -772,21 +668,19 @@
*/
private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()) {
if (enable) {
- mService.enableOptionalCodecs(device);
+ service.enableOptionalCodecs(device);
} else {
- mService.disableOptionalCodecs(device);
+ service.disableOptionalCodecs(device);
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return;
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
return;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -801,17 +695,15 @@
@UnsupportedAppUsage
public int supportsOptionalCodecs(BluetoothDevice device) {
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled() && isValidDevice(device)) {
- return mService.supportsOptionalCodecs(device);
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ return service.supportsOptionalCodecs(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -826,17 +718,15 @@
@UnsupportedAppUsage
public int getOptionalCodecsEnabled(BluetoothDevice device) {
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled() && isValidDevice(device)) {
- return mService.getOptionalCodecsEnabled(device);
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ return service.getOptionalCodecsEnabled(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return OPTIONAL_CODECS_PREF_UNKNOWN;
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e);
return OPTIONAL_CODECS_PREF_UNKNOWN;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -858,18 +748,16 @@
Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
return;
}
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()
&& isValidDevice(device)) {
- mService.setOptionalCodecsEnabled(device, value);
+ service.setOptionalCodecsEnabled(device, value);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -900,35 +788,6 @@
}
}
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "Proxy object connected");
- try {
- mServiceLock.writeLock().lock();
- mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
- } finally {
- mServiceLock.writeLock().unlock();
- }
-
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "Proxy object disconnected");
- try {
- mServiceLock.writeLock().lock();
- mService = null;
- } finally {
- mServiceLock.writeLock().unlock();
- }
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
- }
- }
- };
-
private boolean isEnabled() {
if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
return false;
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index cb996f3..5a8055a 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -17,14 +17,10 @@
package android.bluetooth;
import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -125,93 +121,31 @@
public static final String EXTRA_AUDIO_CONFIG =
"android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG";
- private Context mContext;
- private ServiceListener mServiceListener;
- private volatile IBluetoothA2dpSink mService;
private BluetoothAdapter mAdapter;
-
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
+ private final BluetoothProfileConnector<IBluetoothA2dpSink> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK,
+ "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) {
+ @Override
+ public IBluetoothA2dpSink getServiceInterface(IBinder service) {
+ return IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service));
}
- };
+ };
/**
* Create a BluetoothA2dp proxy object for interacting with the local
* Bluetooth A2DP service.
*/
- /*package*/ BluetoothA2dpSink(Context context, ServiceListener l) {
- mContext = context;
- mServiceListener = l;
+ /*package*/ BluetoothA2dpSink(Context context, ServiceListener listener) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
-
- doBind();
- }
-
- boolean doBind() {
- Intent intent = new Intent(IBluetoothA2dpSink.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
- return false;
- }
- return true;
+ mProfileConnector.connect(context, listener);
}
/*package*/ void close() {
- mServiceListener = null;
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
+ mProfileConnector.disconnect();
+ }
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
+ private IBluetoothA2dpSink getService() {
+ return mProfileConnector.getService();
}
@Override
@@ -242,7 +176,7 @@
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- final IBluetoothA2dpSink service = mService;
+ final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.connect(device);
@@ -283,7 +217,7 @@
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- final IBluetoothA2dpSink service = mService;
+ final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.disconnect(device);
@@ -302,7 +236,7 @@
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothA2dpSink service = mService;
+ final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled()) {
try {
return service.getConnectedDevices();
@@ -321,7 +255,7 @@
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothA2dpSink service = mService;
+ final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled()) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -340,7 +274,7 @@
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- final IBluetoothA2dpSink service = mService;
+ final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getConnectionState(device);
@@ -366,7 +300,7 @@
*/
public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
if (VDBG) log("getAudioConfig(" + device + ")");
- final IBluetoothA2dpSink service = mService;
+ final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getAudioConfig(device);
@@ -396,7 +330,7 @@
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- final IBluetoothA2dpSink service = mService;
+ final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
@@ -428,7 +362,7 @@
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- final IBluetoothA2dpSink service = mService;
+ final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getPriority(device);
@@ -449,7 +383,7 @@
* @param device BluetoothDevice device
*/
public boolean isA2dpPlaying(BluetoothDevice device) {
- final IBluetoothA2dpSink service = mService;
+ final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.isA2dpPlaying(device);
@@ -488,25 +422,6 @@
}
}
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service));
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK,
- BluetoothA2dpSink.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "Proxy object disconnected");
- mService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP_SINK);
- }
- }
- };
-
private boolean isEnabled() {
return mAdapter.getState() == BluetoothAdapter.STATE_ON;
}
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index c447868..4e7e441 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -16,14 +16,10 @@
package android.bluetooth;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -80,93 +76,32 @@
public static final String EXTRA_PLAYER_SETTING =
"android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
- private Context mContext;
- private ServiceListener mServiceListener;
- private volatile IBluetoothAvrcpController mService;
private BluetoothAdapter mAdapter;
-
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
+ private final BluetoothProfileConnector<IBluetoothAvrcpController> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER,
+ "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) {
+ @Override
+ public IBluetoothAvrcpController getServiceInterface(IBinder service) {
+ return IBluetoothAvrcpController.Stub.asInterface(
+ Binder.allowBlocking(service));
}
- };
+ };
/**
* Create a BluetoothAvrcpController proxy object for interacting with the local
* Bluetooth AVRCP service.
*/
- /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) {
- mContext = context;
- mServiceListener = l;
+ /*package*/ BluetoothAvrcpController(Context context, ServiceListener listener) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
-
- doBind();
- }
-
- boolean doBind() {
- Intent intent = new Intent(IBluetoothAvrcpController.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
- return false;
- }
- return true;
+ mProfileConnector.connect(context, listener);
}
/*package*/ void close() {
- mServiceListener = null;
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
+ mProfileConnector.disconnect();
+ }
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
+ private IBluetoothAvrcpController getService() {
+ return mProfileConnector.getService();
}
@Override
@@ -180,7 +115,8 @@
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothAvrcpController service = mService;
+ final IBluetoothAvrcpController service =
+ getService();
if (service != null && isEnabled()) {
try {
return service.getConnectedDevices();
@@ -199,7 +135,8 @@
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothAvrcpController service = mService;
+ final IBluetoothAvrcpController service =
+ getService();
if (service != null && isEnabled()) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -218,7 +155,8 @@
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- final IBluetoothAvrcpController service = mService;
+ final IBluetoothAvrcpController service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getConnectionState(device);
@@ -239,7 +177,8 @@
public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getPlayerSettings");
BluetoothAvrcpPlayerSettings settings = null;
- final IBluetoothAvrcpController service = mService;
+ final IBluetoothAvrcpController service =
+ getService();
if (service != null && isEnabled()) {
try {
settings = service.getPlayerSettings(device);
@@ -257,7 +196,8 @@
*/
public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
- final IBluetoothAvrcpController service = mService;
+ final IBluetoothAvrcpController service =
+ getService();
if (service != null && isEnabled()) {
try {
return service.setPlayerApplicationSetting(plAppSetting);
@@ -277,7 +217,8 @@
public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
+ keyState);
- final IBluetoothAvrcpController service = mService;
+ final IBluetoothAvrcpController service =
+ getService();
if (service != null && isEnabled()) {
try {
service.sendGroupNavigationCmd(device, keyCode, keyState);
@@ -290,25 +231,6 @@
if (service == null) Log.w(TAG, "Proxy not attached to service");
}
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service));
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER,
- BluetoothAvrcpController.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "Proxy object disconnected");
- mService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER);
- }
- }
- };
-
private boolean isEnabled() {
return mAdapter.getState() == BluetoothAdapter.STATE_ON;
}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 8d9d340..9862a63 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -337,19 +337,9 @@
public void onBluetoothStateChange(boolean up) {
if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
doUnbind();
} else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
+ doBind();
}
}
};
@@ -374,24 +364,32 @@
doBind();
}
- boolean doBind() {
- try {
- return mAdapter.getBluetoothManager().bindBluetoothProfileService(
- BluetoothProfile.HEADSET, mConnection);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to bind HeadsetService", e);
+ private boolean doBind() {
+ synchronized (mConnection) {
+ if (mService == null) {
+ if (VDBG) Log.d(TAG, "Binding service...");
+ try {
+ return mAdapter.getBluetoothManager().bindBluetoothProfileService(
+ BluetoothProfile.HEADSET, mConnection);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to bind HeadsetService", e);
+ }
+ }
}
return false;
}
- void doUnbind() {
+ private void doUnbind() {
synchronized (mConnection) {
if (mService != null) {
+ if (VDBG) Log.d(TAG, "Unbinding service...");
try {
mAdapter.getBluetoothManager().unbindBluetoothProfileService(
BluetoothProfile.HEADSET, mConnection);
} catch (RemoteException e) {
Log.e(TAG, "Unable to unbind HeadsetService", e);
+ } finally {
+ mService = null;
}
}
}
@@ -411,8 +409,8 @@
if (mgr != null) {
try {
mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
+ } catch (RemoteException re) {
+ Log.e(TAG, "", re);
}
}
mServiceListener = null;
@@ -1169,7 +1167,7 @@
@Override
public void onServiceDisconnected(ComponentName className) {
if (DBG) Log.d(TAG, "Proxy object disconnected");
- mService = null;
+ doUnbind();
mHandler.sendMessage(mHandler.obtainMessage(
MESSAGE_HEADSET_SERVICE_DISCONNECTED));
}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 549c1fa..05833b5 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -17,15 +17,11 @@
package android.bluetooth;
import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -367,73 +363,22 @@
public static final int CALL_ACCEPT_HOLD = 1;
public static final int CALL_ACCEPT_TERMINATE = 2;
- private Context mContext;
- private ServiceListener mServiceListener;
- private volatile IBluetoothHeadsetClient mService;
private BluetoothAdapter mAdapter;
-
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
+ private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT,
+ "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) {
@Override
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- Intent intent = new Intent(
- IBluetoothHeadsetClient.class.getName());
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
+ public IBluetoothHeadsetClient getServiceInterface(IBinder service) {
+ return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service));
}
- };
+ };
/**
* Create a BluetoothHeadsetClient proxy object.
*/
- /*package*/ BluetoothHeadsetClient(Context context, ServiceListener l) {
- mContext = context;
- mServiceListener = l;
+ /*package*/ BluetoothHeadsetClient(Context context, ServiceListener listener) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
-
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
-
- doBind();
- }
-
- boolean doBind() {
- Intent intent = new Intent(IBluetoothHeadsetClient.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent);
- return false;
- }
- return true;
+ mProfileConnector.connect(context, listener);
}
/**
@@ -444,27 +389,11 @@
*/
/*package*/ void close() {
if (VDBG) log("close()");
+ mProfileConnector.disconnect();
+ }
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
-
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- mServiceListener = null;
+ private IBluetoothHeadsetClient getService() {
+ return mProfileConnector.getService();
}
/**
@@ -481,7 +410,8 @@
@UnsupportedAppUsage
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.connect(device);
@@ -504,7 +434,8 @@
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.disconnect(device);
@@ -525,7 +456,8 @@
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled()) {
try {
return service.getConnectedDevices();
@@ -548,7 +480,8 @@
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled()) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -570,7 +503,8 @@
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getConnectionState(device);
@@ -590,7 +524,8 @@
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
@@ -612,7 +547,8 @@
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getPriority(device);
@@ -638,7 +574,8 @@
*/
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.startVoiceRecognition(device);
@@ -663,7 +600,8 @@
*/
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.stopVoiceRecognition(device);
@@ -683,7 +621,8 @@
*/
public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
if (DBG) log("getCurrentCalls()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getCurrentCalls(device);
@@ -703,7 +642,8 @@
*/
public Bundle getCurrentAgEvents(BluetoothDevice device) {
if (DBG) log("getCurrentCalls()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getCurrentAgEvents(device);
@@ -727,7 +667,8 @@
@UnsupportedAppUsage
public boolean acceptCall(BluetoothDevice device, int flag) {
if (DBG) log("acceptCall()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.acceptCall(device, flag);
@@ -748,7 +689,8 @@
*/
public boolean holdCall(BluetoothDevice device) {
if (DBG) log("holdCall()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.holdCall(device);
@@ -774,7 +716,8 @@
@UnsupportedAppUsage
public boolean rejectCall(BluetoothDevice device) {
if (DBG) log("rejectCall()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.rejectCall(device);
@@ -804,7 +747,8 @@
*/
public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
if (DBG) log("terminateCall()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.terminateCall(device, call);
@@ -832,7 +776,8 @@
*/
public boolean enterPrivateMode(BluetoothDevice device, int index) {
if (DBG) log("enterPrivateMode()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.enterPrivateMode(device, index);
@@ -859,7 +804,8 @@
*/
public boolean explicitCallTransfer(BluetoothDevice device) {
if (DBG) log("explicitCallTransfer()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.explicitCallTransfer(device);
@@ -882,7 +828,8 @@
*/
public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
if (DBG) log("dial()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.dial(device, number);
@@ -906,7 +853,8 @@
*/
public boolean sendDTMF(BluetoothDevice device, byte code) {
if (DBG) log("sendDTMF()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.sendDTMF(device, code);
@@ -932,7 +880,8 @@
*/
public boolean getLastVoiceTagNumber(BluetoothDevice device) {
if (DBG) log("getLastVoiceTagNumber()");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getLastVoiceTagNumber(device);
@@ -952,7 +901,8 @@
@UnsupportedAppUsage
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled()) {
try {
return service.getAudioState(device);
@@ -975,7 +925,8 @@
*/
public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled()) {
try {
service.setAudioRouteAllowed(device, allowed);
@@ -997,7 +948,8 @@
*/
public boolean getAudioRouteAllowed(BluetoothDevice device) {
if (VDBG) log("getAudioRouteAllowed");
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled()) {
try {
return service.getAudioRouteAllowed(device);
@@ -1021,7 +973,8 @@
* otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
*/
public boolean connectAudio(BluetoothDevice device) {
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled()) {
try {
return service.connectAudio(device);
@@ -1045,7 +998,8 @@
* otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
*/
public boolean disconnectAudio(BluetoothDevice device) {
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled()) {
try {
return service.disconnectAudio(device);
@@ -1066,7 +1020,8 @@
* @return bundle of AG features; null if no service or AG not connected
*/
public Bundle getCurrentAgFeatures(BluetoothDevice device) {
- final IBluetoothHeadsetClient service = mService;
+ final IBluetoothHeadsetClient service =
+ getService();
if (service != null && isEnabled()) {
try {
return service.getCurrentAgFeatures(device);
@@ -1080,29 +1035,6 @@
return null;
}
-
- private final ServiceConnection mConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service));
-
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT,
- BluetoothHeadsetClient.this);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "Proxy object disconnected");
- mService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT);
- }
- }
- };
-
private boolean isEnabled() {
return mAdapter.getState() == BluetoothAdapter.STATE_ON;
}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index d6edb90..60fb6fb 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -22,21 +22,14 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
-
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* This class provides the public APIs to control the Hearing Aid profile.
@@ -129,97 +122,31 @@
*/
public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
- private Context mContext;
- private ServiceListener mServiceListener;
- private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
- @GuardedBy("mServiceLock")
- private IBluetoothHearingAid mService;
private BluetoothAdapter mAdapter;
-
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- try {
- mServiceLock.writeLock().lock();
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- } finally {
- mServiceLock.writeLock().unlock();
- }
- } else {
- try {
- mServiceLock.readLock().lock();
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- } finally {
- mServiceLock.readLock().unlock();
- }
- }
+ private final BluetoothProfileConnector<IBluetoothHearingAid> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID,
+ "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) {
+ @Override
+ public IBluetoothHearingAid getServiceInterface(IBinder service) {
+ return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
}
- };
+ };
/**
* Create a BluetoothHearingAid proxy object for interacting with the local
* Bluetooth Hearing Aid service.
*/
- /*package*/ BluetoothHearingAid(Context context, ServiceListener l) {
- mContext = context;
- mServiceListener = l;
+ /*package*/ BluetoothHearingAid(Context context, ServiceListener listener) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
-
- doBind();
- }
-
- void doBind() {
- Intent intent = new Intent(IBluetoothHearingAid.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent);
- return;
- }
+ mProfileConnector.connect(context, listener);
}
/*package*/ void close() {
- mServiceListener = null;
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
+ mProfileConnector.disconnect();
+ }
- try {
- mServiceLock.writeLock().lock();
- if (mService != null) {
- mService = null;
- mContext.unbindService(mConnection);
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- } finally {
- mServiceLock.writeLock().unlock();
- }
+ private IBluetoothHearingAid getService() {
+ return mProfileConnector.getService();
}
/**
@@ -241,18 +168,16 @@
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled() && isValidDevice(device)) {
- return mService.connect(device);
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ return service.connect(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -283,18 +208,16 @@
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled() && isValidDevice(device)) {
- return mService.disconnect(device);
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ return service.disconnect(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -304,18 +227,16 @@
@Override
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- return mService.getConnectedDevices();
+ if (service != null && isEnabled()) {
+ return service.getConnectedDevices();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -326,18 +247,16 @@
public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- return mService.getDevicesMatchingConnectionStates(states);
+ if (service != null && isEnabled()) {
+ return service.getDevicesMatchingConnectionStates(states);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -348,19 +267,17 @@
public @BluetoothProfile.BtProfileState int getConnectionState(
@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ if (service != null && isEnabled()
&& isValidDevice(device)) {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -388,20 +305,18 @@
*/
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ if (service != null && isEnabled()
&& ((device == null) || isValidDevice(device))) {
- mService.setActiveDevice(device);
+ service.setActiveDevice(device);
return true;
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -419,18 +334,16 @@
@RequiresPermission(Manifest.permission.BLUETOOTH)
public List<BluetoothDevice> getActiveDevices() {
if (VDBG) log("getActiveDevices()");
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- return mService.getActiveDevices();
+ if (service != null && isEnabled()) {
+ return service.getActiveDevices();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<>();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<>();
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -451,23 +364,21 @@
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ if (service != null && isEnabled()
&& isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
- return mService.setPriority(device, priority);
+ return service.setPriority(device, priority);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -485,19 +396,17 @@
@RequiresPermission(Manifest.permission.BLUETOOTH)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ if (service != null && isEnabled()
&& isValidDevice(device)) {
- return mService.getPriority(device);
+ return service.getPriority(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.PRIORITY_OFF;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.PRIORITY_OFF;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -536,18 +445,16 @@
if (VDBG) {
log("getVolume()");
}
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()) {
- return mService.getVolume();
+ if (service != null && isEnabled()) {
+ return service.getVolume();
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return 0;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return 0;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -567,21 +474,18 @@
public void adjustVolume(int direction) {
if (DBG) log("adjustVolume(" + direction + ")");
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
-
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
return;
}
if (!isEnabled()) return;
- mService.adjustVolume(direction);
+ service.adjustVolume(direction);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -594,20 +498,18 @@
public void setVolume(int volume) {
if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
return;
}
if (!isEnabled()) return;
- mService.setVolume(volume);
+ service.setVolume(volume);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -623,21 +525,19 @@
if (VDBG) {
log("getCustomerId(" + device + ")");
}
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
return HI_SYNC_ID_INVALID;
}
if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
- return mService.getHiSyncId(device);
+ return service.getHiSyncId(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return HI_SYNC_ID_INVALID;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -653,19 +553,17 @@
if (VDBG) {
log("getDeviceSide(" + device + ")");
}
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ if (service != null && isEnabled()
&& isValidDevice(device)) {
- return mService.getDeviceSide(device);
+ return service.getDeviceSide(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return SIDE_LEFT;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return SIDE_LEFT;
- } finally {
- mServiceLock.readLock().unlock();
}
}
@@ -681,52 +579,20 @@
if (VDBG) {
log("getDeviceMode(" + device + ")");
}
+ final IBluetoothHearingAid service = getService();
try {
- mServiceLock.readLock().lock();
- if (mService != null && isEnabled()
+ if (service != null && isEnabled()
&& isValidDevice(device)) {
- return mService.getDeviceMode(device);
+ return service.getDeviceMode(device);
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return MODE_MONAURAL;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return MODE_MONAURAL;
- } finally {
- mServiceLock.readLock().unlock();
}
}
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "Proxy object connected");
- try {
- mServiceLock.writeLock().lock();
- mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
- } finally {
- mServiceLock.writeLock().unlock();
- }
-
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID,
- BluetoothHearingAid.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "Proxy object disconnected");
- try {
- mServiceLock.writeLock().lock();
- mService = null;
- } finally {
- mServiceLock.writeLock().unlock();
- }
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID);
- }
- }
- };
-
private boolean isEnabled() {
if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
return false;
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index e44f36e..e9b0be2 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -18,13 +18,10 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -328,11 +325,6 @@
}
}
- private Context mContext;
- private ServiceListener mServiceListener;
- private volatile IBluetoothHidDevice mService;
- private BluetoothAdapter mAdapter;
-
private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub {
private final Executor mExecutor;
@@ -386,114 +378,33 @@
}
}
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
-
- public void onBluetoothStateChange(boolean up) {
- Log.d(TAG, "onBluetoothStateChange: up=" + up);
- synchronized (mConnection) {
- if (up) {
- try {
- if (mService == null) {
- Log.d(TAG, "Binding HID Device service...");
- doBind();
- }
- } catch (IllegalStateException e) {
- Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev "
- + "service: ", e);
- } catch (SecurityException e) {
- Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev "
- + "service: ", e);
- }
- } else {
- Log.d(TAG, "Unbinding service...");
- doUnbind();
- }
- }
+ private BluetoothAdapter mAdapter;
+ private final BluetoothProfileConnector<IBluetoothHidDevice> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE,
+ "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) {
+ @Override
+ public IBluetoothHidDevice getServiceInterface(IBinder service) {
+ return IBluetoothHidDevice.Stub.asInterface(Binder.allowBlocking(service));
}
- };
-
- private final ServiceConnection mConnection =
- new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- Log.d(TAG, "onServiceConnected()");
- mService = IBluetoothHidDevice.Stub.asInterface(service);
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(
- BluetoothProfile.HID_DEVICE, BluetoothHidDevice.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- Log.d(TAG, "onServiceDisconnected()");
- mService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE);
- }
- }
- };
+ };
BluetoothHidDevice(Context context, ServiceListener listener) {
- mContext = context;
- mServiceListener = listener;
mAdapter = BluetoothAdapter.getDefaultAdapter();
-
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- doBind();
- }
-
- boolean doBind() {
- Intent intent = new Intent(IBluetoothHidDevice.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent);
- return false;
- }
- Log.d(TAG, "Bound to HID Device Service");
- return true;
- }
-
- void doUnbind() {
- if (mService != null) {
- mService = null;
- try {
- mContext.unbindService(mConnection);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Unable to unbind HidDevService", e);
- }
- }
+ mProfileConnector.connect(context, listener);
}
void close() {
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
+ mProfileConnector.disconnect();
+ }
- synchronized (mConnection) {
- doUnbind();
- }
- mServiceListener = null;
+ private IBluetoothHidDevice getService() {
+ return mProfileConnector.getService();
}
/** {@inheritDoc} */
@Override
public List<BluetoothDevice> getConnectedDevices() {
- final IBluetoothHidDevice service = mService;
+ final IBluetoothHidDevice service = getService();
if (service != null) {
try {
return service.getConnectedDevices();
@@ -510,7 +421,7 @@
/** {@inheritDoc} */
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
- final IBluetoothHidDevice service = mService;
+ final IBluetoothHidDevice service = getService();
if (service != null) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -527,7 +438,7 @@
/** {@inheritDoc} */
@Override
public int getConnectionState(BluetoothDevice device) {
- final IBluetoothHidDevice service = mService;
+ final IBluetoothHidDevice service = getService();
if (service != null) {
try {
return service.getConnectionState(device);
@@ -584,7 +495,7 @@
throw new IllegalArgumentException("callback parameter cannot be null");
}
- final IBluetoothHidDevice service = mService;
+ final IBluetoothHidDevice service = getService();
if (service != null) {
try {
CallbackWrapper cbw = new CallbackWrapper(executor, callback);
@@ -612,7 +523,7 @@
public boolean unregisterApp() {
boolean result = false;
- final IBluetoothHidDevice service = mService;
+ final IBluetoothHidDevice service = getService();
if (service != null) {
try {
result = service.unregisterApp();
@@ -637,7 +548,7 @@
public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
boolean result = false;
- final IBluetoothHidDevice service = mService;
+ final IBluetoothHidDevice service = getService();
if (service != null) {
try {
result = service.sendReport(device, id, data);
@@ -663,7 +574,7 @@
public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
boolean result = false;
- final IBluetoothHidDevice service = mService;
+ final IBluetoothHidDevice service = getService();
if (service != null) {
try {
result = service.replyReport(device, type, id, data);
@@ -687,7 +598,7 @@
public boolean reportError(BluetoothDevice device, byte error) {
boolean result = false;
- final IBluetoothHidDevice service = mService;
+ final IBluetoothHidDevice service = getService();
if (service != null) {
try {
result = service.reportError(device, error);
@@ -708,7 +619,7 @@
* {@hide}
*/
public String getUserAppName() {
- final IBluetoothHidDevice service = mService;
+ final IBluetoothHidDevice service = getService();
if (service != null) {
try {
@@ -734,7 +645,7 @@
public boolean connect(BluetoothDevice device) {
boolean result = false;
- final IBluetoothHidDevice service = mService;
+ final IBluetoothHidDevice service = getService();
if (service != null) {
try {
result = service.connect(device);
@@ -758,7 +669,7 @@
public boolean disconnect(BluetoothDevice device) {
boolean result = false;
- final IBluetoothHidDevice service = mService;
+ final IBluetoothHidDevice service = getService();
if (service != null) {
try {
result = service.disconnect(device);
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 7c925a1..4afb382 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -18,14 +18,10 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -220,97 +216,32 @@
public static final String EXTRA_IDLE_TIME =
"android.bluetooth.BluetoothHidHost.extra.IDLE_TIME";
- private Context mContext;
- private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
- private volatile IBluetoothHidHost mService;
-
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- synchronized (mConnection) {
- try {
- if (mService != null) {
- mService = null;
- mContext.unbindService(mConnection);
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
+ private final BluetoothProfileConnector<IBluetoothHidHost> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST,
+ "BluetoothHidHost", IBluetoothHidHost.class.getName()) {
+ @Override
+ public IBluetoothHidHost getServiceInterface(IBinder service) {
+ return IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service));
}
- };
+ };
/**
* Create a BluetoothHidHost proxy object for interacting with the local
* Bluetooth Service which handles the InputDevice profile
*/
- /*package*/ BluetoothHidHost(Context context, ServiceListener l) {
- mContext = context;
- mServiceListener = l;
+ /*package*/ BluetoothHidHost(Context context, ServiceListener listener) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
-
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
-
- doBind();
- }
-
- boolean doBind() {
- Intent intent = new Intent(IBluetoothHidHost.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent);
- return false;
- }
- return true;
+ mProfileConnector.connect(context, listener);
}
/*package*/ void close() {
if (VDBG) log("close()");
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
+ mProfileConnector.disconnect();
+ }
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- mServiceListener = null;
+ private IBluetoothHidHost getService() {
+ return mProfileConnector.getService();
}
/**
@@ -334,7 +265,7 @@
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.connect(device);
@@ -374,7 +305,7 @@
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.disconnect(device);
@@ -393,7 +324,7 @@
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled()) {
try {
return service.getConnectedDevices();
@@ -412,7 +343,7 @@
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled()) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -431,7 +362,7 @@
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getConnectionState(device);
@@ -461,7 +392,7 @@
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
@@ -493,7 +424,7 @@
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getPriority(device);
@@ -506,26 +437,6 @@
return BluetoothProfile.PRIORITY_OFF;
}
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service));
-
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST,
- BluetoothHidHost.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "Proxy object disconnected");
- mService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST);
- }
- }
- };
-
private boolean isEnabled() {
return mAdapter.getState() == BluetoothAdapter.STATE_ON;
}
@@ -545,7 +456,7 @@
*/
public boolean virtualUnplug(BluetoothDevice device) {
if (DBG) log("virtualUnplug(" + device + ")");
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.virtualUnplug(device);
@@ -571,7 +482,7 @@
*/
public boolean getProtocolMode(BluetoothDevice device) {
if (VDBG) log("getProtocolMode(" + device + ")");
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getProtocolMode(device);
@@ -595,7 +506,7 @@
*/
public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
if (DBG) log("setProtocolMode(" + device + ")");
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.setProtocolMode(device, protocolMode);
@@ -626,7 +537,7 @@
log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId
+ "bufferSize=" + bufferSize);
}
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getReport(device, reportType, reportId, bufferSize);
@@ -652,7 +563,7 @@
*/
public boolean setReport(BluetoothDevice device, byte reportType, String report) {
if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.setReport(device, reportType, report);
@@ -677,7 +588,7 @@
*/
public boolean sendData(BluetoothDevice device, String report) {
if (DBG) log("sendData(" + device + "), report=" + report);
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.sendData(device, report);
@@ -701,7 +612,7 @@
*/
public boolean getIdleTime(BluetoothDevice device) {
if (DBG) log("getIdletime(" + device + ")");
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getIdleTime(device);
@@ -726,7 +637,7 @@
*/
public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
- final IBluetoothHidHost service = mService;
+ final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.setIdleTime(device, idleTime);
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index fc5f830..dd2f150 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -17,14 +17,10 @@
package android.bluetooth;
import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -45,11 +41,6 @@
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
- private volatile IBluetoothMap mService;
- private final Context mContext;
- private ServiceListener mServiceListener;
- private BluetoothAdapter mAdapter;
-
/** There was an error trying to obtain the state */
public static final int STATE_ERROR = -1;
@@ -58,64 +49,23 @@
/** Connection canceled before completion. */
public static final int RESULT_CANCELED = 2;
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
+ private BluetoothAdapter mAdapter;
+ private final BluetoothProfileConnector<IBluetoothMap> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.MAP,
+ "BluetoothMap", IBluetoothMap.class.getName()) {
+ @Override
+ public IBluetoothMap getServiceInterface(IBinder service) {
+ return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service));
}
- };
+ };
/**
* Create a BluetoothMap proxy object.
*/
- /*package*/ BluetoothMap(Context context, ServiceListener l) {
+ /*package*/ BluetoothMap(Context context, ServiceListener listener) {
if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
- mContext = context;
- mServiceListener = l;
mAdapter = BluetoothAdapter.getDefaultAdapter();
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- doBind();
- }
-
- boolean doBind() {
- Intent intent = new Intent(IBluetoothMap.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent);
- return false;
- }
- return true;
+ mProfileConnector.connect(context, listener);
}
protected void finalize() throws Throwable {
@@ -133,26 +83,11 @@
* are ok.
*/
public synchronized void close() {
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
+ mProfileConnector.disconnect();
+ }
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- mServiceListener = null;
+ private IBluetoothMap getService() {
+ return mProfileConnector.getService();
}
/**
@@ -163,7 +98,7 @@
*/
public int getState() {
if (VDBG) log("getState()");
- final IBluetoothMap service = mService;
+ final IBluetoothMap service = getService();
if (service != null) {
try {
return service.getState();
@@ -185,7 +120,7 @@
*/
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
- final IBluetoothMap service = mService;
+ final IBluetoothMap service = getService();
if (service != null) {
try {
return service.getClient();
@@ -206,7 +141,7 @@
*/
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
- final IBluetoothMap service = mService;
+ final IBluetoothMap service = getService();
if (service != null) {
try {
return service.isConnected(device);
@@ -238,7 +173,7 @@
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- final IBluetoothMap service = mService;
+ final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.disconnect(device);
@@ -279,7 +214,7 @@
*/
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
- final IBluetoothMap service = mService;
+ final IBluetoothMap service = getService();
if (service != null && isEnabled()) {
try {
return service.getConnectedDevices();
@@ -299,7 +234,7 @@
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
- final IBluetoothMap service = mService;
+ final IBluetoothMap service = getService();
if (service != null && isEnabled()) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -319,7 +254,7 @@
*/
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
- final IBluetoothMap service = mService;
+ final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getConnectionState(device);
@@ -345,7 +280,7 @@
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- final IBluetoothMap service = mService;
+ final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
@@ -374,7 +309,7 @@
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- final IBluetoothMap service = mService;
+ final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getPriority(device);
@@ -387,24 +322,6 @@
return PRIORITY_OFF;
}
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) log("Proxy object connected");
- mService = IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service));
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) log("Proxy object disconnected");
- mService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.MAP);
- }
- }
- };
-
private static void log(String msg) {
Log.d(TAG, msg);
}
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 1c82e19..ec0180c5 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -18,14 +18,11 @@
import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.net.Uri;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -61,11 +58,6 @@
public static final String EXTRA_SENDER_CONTACT_NAME =
"android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME";
- private volatile IBluetoothMapClient mService;
- private final Context mContext;
- private ServiceListener mServiceListener;
- private BluetoothAdapter mAdapter;
-
/** There was an error trying to obtain the state */
public static final int STATE_ERROR = -1;
@@ -76,64 +68,23 @@
private static final int UPLOADING_FEATURE_BITMASK = 0x08;
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
+ private BluetoothAdapter mAdapter;
+ private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT,
+ "BluetoothMapClient", IBluetoothMapClient.class.getName()) {
+ @Override
+ public IBluetoothMapClient getServiceInterface(IBinder service) {
+ return IBluetoothMapClient.Stub.asInterface(Binder.allowBlocking(service));
}
- };
+ };
/**
* Create a BluetoothMapClient proxy object.
*/
- /*package*/ BluetoothMapClient(Context context, ServiceListener l) {
+ /*package*/ BluetoothMapClient(Context context, ServiceListener listener) {
if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object");
- mContext = context;
- mServiceListener = l;
mAdapter = BluetoothAdapter.getDefaultAdapter();
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- doBind();
- }
-
- boolean doBind() {
- Intent intent = new Intent(IBluetoothMapClient.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent);
- return false;
- }
- return true;
+ mProfileConnector.connect(context, listener);
}
protected void finalize() throws Throwable {
@@ -151,26 +102,11 @@
* are ok.
*/
public void close() {
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
+ mProfileConnector.disconnect();
+ }
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- mServiceListener = null;
+ private IBluetoothMapClient getService() {
+ return mProfileConnector.getService();
}
/**
@@ -180,7 +116,7 @@
*/
public boolean isConnected(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
- final IBluetoothMapClient service = mService;
+ final IBluetoothMapClient service = getService();
if (service != null) {
try {
return service.isConnected(device);
@@ -200,7 +136,7 @@
*/
public boolean connect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
- final IBluetoothMapClient service = mService;
+ final IBluetoothMapClient service = getService();
if (service != null) {
try {
return service.connect(device);
@@ -222,7 +158,7 @@
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "disconnect(" + device + ")");
- final IBluetoothMapClient service = mService;
+ final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.disconnect(device);
@@ -242,7 +178,7 @@
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) Log.d(TAG, "getConnectedDevices()");
- final IBluetoothMapClient service = mService;
+ final IBluetoothMapClient service = getService();
if (service != null && isEnabled()) {
try {
return service.getConnectedDevices();
@@ -263,7 +199,7 @@
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
- final IBluetoothMapClient service = mService;
+ final IBluetoothMapClient service = getService();
if (service != null && isEnabled()) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -284,7 +220,7 @@
@Override
public int getConnectionState(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
- final IBluetoothMapClient service = mService;
+ final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getConnectionState(device);
@@ -308,7 +244,7 @@
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
- final IBluetoothMapClient service = mService;
+ final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
@@ -337,7 +273,7 @@
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
- final IBluetoothMapClient service = mService;
+ final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getPriority(device);
@@ -366,7 +302,7 @@
public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
PendingIntent sentIntent, PendingIntent deliveredIntent) {
if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
- final IBluetoothMapClient service = mService;
+ final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
@@ -386,7 +322,7 @@
*/
public boolean getUnreadMessages(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
- final IBluetoothMapClient service = mService;
+ final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getUnreadMessages(device);
@@ -406,34 +342,16 @@
* MapSupportedFeatures field is set. False is returned otherwise.
*/
public boolean isUploadingSupported(BluetoothDevice device) {
+ final IBluetoothMapClient service = getService();
try {
- return (mService != null && isEnabled() && isValidDevice(device))
- && ((mService.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0);
+ return (service != null && isEnabled() && isValidDevice(device))
+ && ((service.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage());
}
return false;
}
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothMapClient.Stub.asInterface(service);
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT,
- BluetoothMapClient.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "Proxy object disconnected");
- mService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.MAP_CLIENT);
- }
- }
- };
-
private boolean isEnabled() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 8923d73..fb78789 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -19,14 +19,10 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -122,108 +118,42 @@
*/
public static final int PAN_OPERATION_SUCCESS = 1004;
- private Context mContext;
- private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
- private volatile IBluetoothPan mPanService;
+ private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.PAN,
+ "BluetoothPan", IBluetoothPan.class.getName()) {
+ @Override
+ public IBluetoothPan getServiceInterface(IBinder service) {
+ return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service));
+ }
+ };
+
/**
* Create a BluetoothPan proxy object for interacting with the local
* Bluetooth Service which handles the Pan profile
*/
@UnsupportedAppUsage
- /*package*/ BluetoothPan(Context context, ServiceListener l) {
- mContext = context;
- mServiceListener = l;
+ /*package*/ BluetoothPan(Context context, ServiceListener listener) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
- try {
- mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback);
- } catch (RemoteException re) {
- Log.w(TAG, "Unable to register BluetoothStateChangeCallback", re);
- }
- if (VDBG) Log.d(TAG, "BluetoothPan() call bindService");
- doBind();
- }
-
- @UnsupportedAppUsage
- boolean doBind() {
- Intent intent = new Intent(IBluetoothPan.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent);
- return false;
- }
- return true;
+ mProfileConnector.connect(context, listener);
}
@UnsupportedAppUsage
/*package*/ void close() {
if (VDBG) log("close()");
-
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mStateChangeCallback);
- } catch (RemoteException re) {
- Log.w(TAG, "Unable to unregister BluetoothStateChangeCallback", re);
- }
- }
-
- synchronized (mConnection) {
- if (mPanService != null) {
- try {
- mPanService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- mServiceListener = null;
+ mProfileConnector.disconnect();
}
+ private IBluetoothPan getService() {
+ return mProfileConnector.getService();
+ }
+
+
protected void finalize() {
close();
}
- private final IBluetoothStateChangeCallback mStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
-
- @Override
- public void onBluetoothStateChange(boolean on) {
- // Handle enable request to bind again.
- Log.d(TAG, "onBluetoothStateChange on: " + on);
- if (on) {
- try {
- if (mPanService == null) {
- if (VDBG) Log.d(TAG, "onBluetoothStateChange calling doBind()");
- doBind();
- }
-
- } catch (IllegalStateException e) {
- Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ",
- e);
-
- } catch (SecurityException e) {
- Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ",
- e);
- }
- } else {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- synchronized (mConnection) {
- try {
- mPanService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- }
- };
-
/**
* Initiate connection to a profile of the remote bluetooth device.
*
@@ -244,7 +174,7 @@
@UnsupportedAppUsage
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- final IBluetoothPan service = mPanService;
+ final IBluetoothPan service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.connect(device);
@@ -285,7 +215,7 @@
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- final IBluetoothPan service = mPanService;
+ final IBluetoothPan service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.disconnect(device);
@@ -304,7 +234,7 @@
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothPan service = mPanService;
+ final IBluetoothPan service = getService();
if (service != null && isEnabled()) {
try {
return service.getConnectedDevices();
@@ -323,7 +253,7 @@
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothPan service = mPanService;
+ final IBluetoothPan service = getService();
if (service != null && isEnabled()) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -342,7 +272,7 @@
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- final IBluetoothPan service = mPanService;
+ final IBluetoothPan service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getConnectionState(device);
@@ -358,7 +288,7 @@
@UnsupportedAppUsage
public void setBluetoothTethering(boolean value) {
if (DBG) log("setBluetoothTethering(" + value + ")");
- final IBluetoothPan service = mPanService;
+ final IBluetoothPan service = getService();
if (service != null && isEnabled()) {
try {
service.setBluetoothTethering(value);
@@ -371,7 +301,7 @@
@UnsupportedAppUsage
public boolean isTetheringOn() {
if (VDBG) log("isTetheringOn()");
- final IBluetoothPan service = mPanService;
+ final IBluetoothPan service = getService();
if (service != null && isEnabled()) {
try {
return service.isTetheringOn();
@@ -382,25 +312,6 @@
return false;
}
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected");
- mPanService = IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service));
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.PAN,
- BluetoothPan.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected");
- mPanService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.PAN);
- }
- }
- };
-
@UnsupportedAppUsage
private boolean isEnabled() {
return mAdapter.getState() == BluetoothAdapter.STATE_ON;
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 359bec6..d94c657 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -118,28 +118,9 @@
public void onBluetoothStateChange(boolean up) {
log("onBluetoothStateChange: up=" + up);
if (!up) {
- log("Unbinding service...");
- synchronized (mConnection) {
- try {
- if (mService != null) {
- mService = null;
- mContext.unbindService(mConnection);
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
+ doUnbind();
} else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- log("Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
+ doBind();
}
}
};
@@ -155,25 +136,51 @@
if (mgr != null) {
try {
mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
+ } catch (RemoteException re) {
+ Log.e(TAG, "", re);
}
}
doBind();
}
boolean doBind() {
- Intent intent = new Intent(IBluetoothPbap.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
- return false;
+ synchronized (mConnection) {
+ try {
+ if (mService == null) {
+ log("Binding service...");
+ Intent intent = new Intent(IBluetoothPbap.class.getName());
+ ComponentName comp = intent.resolveSystemService(
+ mContext.getPackageManager(), 0);
+ intent.setComponent(comp);
+ if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+ UserHandle.CURRENT_OR_SELF)) {
+ Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
+ return false;
+ }
+ }
+ } catch (SecurityException se) {
+ Log.e(TAG, "", se);
+ return false;
+ }
}
return true;
}
+ private void doUnbind() {
+ synchronized (mConnection) {
+ if (mService != null) {
+ log("Unbinding service...");
+ try {
+ mContext.unbindService(mConnection);
+ } catch (IllegalArgumentException ie) {
+ Log.e(TAG, "", ie);
+ } finally {
+ mService = null;
+ }
+ }
+ }
+ }
+
protected void finalize() throws Throwable {
try {
close();
@@ -193,21 +200,11 @@
if (mgr != null) {
try {
mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
+ } catch (RemoteException re) {
+ Log.e(TAG, "", re);
}
}
-
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
+ doUnbind();
mServiceListener = null;
}
@@ -313,7 +310,7 @@
public void onServiceDisconnected(ComponentName className) {
log("Proxy object disconnected");
- mService = null;
+ doUnbind();
if (mServiceListener != null) {
mServiceListener.onServiceDisconnected();
}
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index cbc96c0..d70e1e7 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -16,14 +16,10 @@
package android.bluetooth;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -43,11 +39,6 @@
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
- private volatile IBluetoothPbapClient mService;
- private final Context mContext;
- private ServiceListener mServiceListener;
- private BluetoothAdapter mAdapter;
-
/** There was an error trying to obtain the state */
public static final int STATE_ERROR = -1;
@@ -56,72 +47,25 @@
/** Connection canceled before completion. */
public static final int RESULT_CANCELED = 2;
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) {
- Log.d(TAG, "onBluetoothStateChange: PBAP CLIENT up=" + up);
- }
- if (!up) {
- if (VDBG) {
- Log.d(TAG, "Unbinding service...");
- }
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) {
- Log.d(TAG, "Binding service...");
- }
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
+ private BluetoothAdapter mAdapter;
+ private final BluetoothProfileConnector<IBluetoothPbapClient> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.PBAP_CLIENT,
+ "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) {
+ @Override
+ public IBluetoothPbapClient getServiceInterface(IBinder service) {
+ return IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service));
}
- };
+ };
/**
* Create a BluetoothPbapClient proxy object.
*/
- BluetoothPbapClient(Context context, ServiceListener l) {
+ BluetoothPbapClient(Context context, ServiceListener listener) {
if (DBG) {
Log.d(TAG, "Create BluetoothPbapClient proxy object");
}
- mContext = context;
- mServiceListener = l;
mAdapter = BluetoothAdapter.getDefaultAdapter();
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- doBind();
- }
-
- private boolean doBind() {
- Intent intent = new Intent(IBluetoothPbapClient.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent);
- return false;
- }
- return true;
+ mProfileConnector.connect(context, listener);
}
protected void finalize() throws Throwable {
@@ -139,26 +83,11 @@
* are ok.
*/
public synchronized void close() {
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
+ mProfileConnector.disconnect();
+ }
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- mServiceListener = null;
+ private IBluetoothPbapClient getService() {
+ return mProfileConnector.getService();
}
/**
@@ -174,7 +103,7 @@
if (DBG) {
log("connect(" + device + ") for PBAP Client.");
}
- final IBluetoothPbapClient service = mService;
+ final IBluetoothPbapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.connect(device);
@@ -199,7 +128,7 @@
if (DBG) {
log("disconnect(" + device + ")" + new Exception());
}
- final IBluetoothPbapClient service = mService;
+ final IBluetoothPbapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
service.disconnect(device);
@@ -226,7 +155,7 @@
if (DBG) {
log("getConnectedDevices()");
}
- final IBluetoothPbapClient service = mService;
+ final IBluetoothPbapClient service = getService();
if (service != null && isEnabled()) {
try {
return service.getConnectedDevices();
@@ -251,7 +180,7 @@
if (DBG) {
log("getDevicesMatchingStates()");
}
- final IBluetoothPbapClient service = mService;
+ final IBluetoothPbapClient service = getService();
if (service != null && isEnabled()) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -276,7 +205,7 @@
if (DBG) {
log("getConnectionState(" + device + ")");
}
- final IBluetoothPbapClient service = mService;
+ final IBluetoothPbapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getConnectionState(device);
@@ -291,29 +220,6 @@
return BluetoothProfile.STATE_DISCONNECTED;
}
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) {
- log("Proxy object connected");
- }
- mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service));
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT,
- BluetoothPbapClient.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) {
- log("Proxy object disconnected");
- }
- mService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP_CLIENT);
- }
- }
- };
-
private static void log(String msg) {
Log.d(TAG, msg);
}
@@ -346,7 +252,7 @@
if (DBG) {
log("setPriority(" + device + ", " + priority + ")");
}
- final IBluetoothPbapClient service = mService;
+ final IBluetoothPbapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
@@ -379,7 +285,7 @@
if (VDBG) {
log("getPriority(" + device + ")");
}
- final IBluetoothPbapClient service = mService;
+ final IBluetoothPbapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getPriority(device);
diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java
new file mode 100644
index 0000000..d9987249
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothProfileConnector.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 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 android.bluetooth;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * Connector for Bluetooth profile proxies to bind manager service and
+ * profile services
+ * @param <T> The Bluetooth profile interface for this connection.
+ * @hide
+ */
+public abstract class BluetoothProfileConnector<T> {
+ private int mProfileId;
+ private BluetoothProfile.ServiceListener mServiceListener;
+ private BluetoothProfile mProfileProxy;
+ private Context mContext;
+ private String mProfileName;
+ private String mServiceName;
+ private volatile T mService;
+
+ private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (up) {
+ doBind();
+ } else {
+ doUnbind();
+ }
+ }
+ };
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ logDebug("Proxy object connected");
+ mService = getServiceInterface(service);
+
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected(mProfileId, mProfileProxy);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ logDebug("Proxy object disconnected");
+ doUnbind();
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
+ }
+ }
+ };
+
+ BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName,
+ String serviceName) {
+ mProfileId = profileId;
+ mProfileProxy = profile;
+ mProfileName = profileName;
+ mServiceName = serviceName;
+ }
+
+ private boolean doBind() {
+ synchronized (mConnection) {
+ if (mService == null) {
+ logDebug("Binding service...");
+ try {
+ Intent intent = new Intent(mServiceName);
+ ComponentName comp = intent.resolveSystemService(
+ mContext.getPackageManager(), 0);
+ intent.setComponent(comp);
+ if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+ UserHandle.CURRENT_OR_SELF)) {
+ logError("Could not bind to Bluetooth Service with " + intent);
+ return false;
+ }
+ } catch (SecurityException se) {
+ logError("Failed to bind service. " + se);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private void doUnbind() {
+ synchronized (mConnection) {
+ if (mService != null) {
+ logDebug("Unbinding service...");
+ try {
+ mContext.unbindService(mConnection);
+ } catch (IllegalArgumentException ie) {
+ logError("Unable to unbind service: " + ie);
+ } finally {
+ mService = null;
+ }
+ }
+ }
+ }
+
+ void connect(Context context, BluetoothProfile.ServiceListener listener) {
+ mContext = context;
+ mServiceListener = listener;
+ IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException re) {
+ logError("Failed to register state change callback. " + re);
+ }
+ }
+ doBind();
+ }
+
+ void disconnect() {
+ mServiceListener = null;
+ IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException re) {
+ logError("Failed to unregister state change callback" + re);
+ }
+ }
+ doUnbind();
+ }
+
+ T getService() {
+ return mService;
+ }
+
+ /**
+ * This abstract function is used to implement method to get the
+ * connected Bluetooth service interface.
+ * @param service the connected binder service.
+ * @return T the binder interface of {@code service}.
+ * @hide
+ */
+ public abstract T getServiceInterface(IBinder service);
+
+ private void logDebug(String log) {
+ Log.d(mProfileName, log);
+ }
+
+ private void logError(String log) {
+ Log.e(mProfileName, log);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index ebf6bed..e0610c8 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -17,14 +17,10 @@
package android.bluetooth;
import android.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -70,11 +66,6 @@
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
- private volatile IBluetoothSap mService;
- private final Context mContext;
- private ServiceListener mServiceListener;
- private BluetoothAdapter mAdapter;
-
/**
* There was an error trying to obtain the state.
*
@@ -96,64 +87,23 @@
*/
public static final int RESULT_CANCELED = 2;
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
+ private BluetoothAdapter mAdapter;
+ private final BluetoothProfileConnector<IBluetoothSap> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.SAP,
+ "BluetoothSap", IBluetoothSap.class.getName()) {
+ @Override
+ public IBluetoothSap getServiceInterface(IBinder service) {
+ return IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
}
- };
+ };
/**
* Create a BluetoothSap proxy object.
*/
- /*package*/ BluetoothSap(Context context, ServiceListener l) {
+ /*package*/ BluetoothSap(Context context, ServiceListener listener) {
if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
- mContext = context;
- mServiceListener = l;
mAdapter = BluetoothAdapter.getDefaultAdapter();
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- doBind();
- }
-
- boolean doBind() {
- Intent intent = new Intent(IBluetoothSap.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
- return false;
- }
- return true;
+ mProfileConnector.connect(context, listener);
}
protected void finalize() throws Throwable {
@@ -173,26 +123,11 @@
* @hide
*/
public synchronized void close() {
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
+ mProfileConnector.disconnect();
+ }
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- mServiceListener = null;
+ private IBluetoothSap getService() {
+ return mProfileConnector.getService();
}
/**
@@ -204,7 +139,7 @@
*/
public int getState() {
if (VDBG) log("getState()");
- final IBluetoothSap service = mService;
+ final IBluetoothSap service = getService();
if (service != null) {
try {
return service.getState();
@@ -227,7 +162,7 @@
*/
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
- final IBluetoothSap service = mService;
+ final IBluetoothSap service = getService();
if (service != null) {
try {
return service.getClient();
@@ -250,7 +185,7 @@
*/
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
- final IBluetoothSap service = mService;
+ final IBluetoothSap service = getService();
if (service != null) {
try {
return service.isConnected(device);
@@ -285,7 +220,7 @@
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- final IBluetoothSap service = mService;
+ final IBluetoothSap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.disconnect(device);
@@ -306,7 +241,7 @@
*/
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
- final IBluetoothSap service = mService;
+ final IBluetoothSap service = getService();
if (service != null && isEnabled()) {
try {
return service.getConnectedDevices();
@@ -327,7 +262,7 @@
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
- final IBluetoothSap service = mService;
+ final IBluetoothSap service = getService();
if (service != null && isEnabled()) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -348,7 +283,7 @@
*/
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
- final IBluetoothSap service = mService;
+ final IBluetoothSap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getConnectionState(device);
@@ -373,7 +308,7 @@
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- final IBluetoothSap service = mService;
+ final IBluetoothSap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
@@ -399,7 +334,7 @@
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- final IBluetoothSap service = mService;
+ final IBluetoothSap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getPriority(device);
@@ -412,24 +347,6 @@
return PRIORITY_OFF;
}
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) log("Proxy object connected");
- mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) log("Proxy object disconnected");
- mService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.SAP);
- }
- }
- };
-
private static void log(String msg) {
Log.d(TAG, msg);
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 338eb2d..a8815ec 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1318,6 +1318,8 @@
public boolean isMultiPackage;
/** {@hide} */
public boolean isStaged;
+ /** {@hide} */
+ public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
/**
* Construct parameters for a new package install session.
@@ -1350,6 +1352,7 @@
installerPackageName = source.readString();
isMultiPackage = source.readBoolean();
isStaged = source.readBoolean();
+ requiredInstalledVersionCode = source.readLong();
}
/** {@hide} */
@@ -1372,6 +1375,7 @@
ret.installerPackageName = installerPackageName;
ret.isMultiPackage = isMultiPackage;
ret.isStaged = isStaged;
+ ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
return ret;
}
@@ -1562,6 +1566,19 @@
}
}
+ /**
+ * Require the given version of the package be installed.
+ * The install will only be allowed if the existing version code of
+ * the package installed on the device matches the given version code.
+ * Use {@link * PackageManager#VERSION_CODE_HIGHEST} to allow
+ * installation regardless of the currently installed package version.
+ *
+ * @hide
+ */
+ public void setRequiredInstalledVersionCode(long versionCode) {
+ requiredInstalledVersionCode = versionCode;
+ }
+
/** {@hide} */
public void setInstallFlagsForcePermissionPrompt() {
installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
@@ -1703,6 +1720,7 @@
pw.printPair("installerPackageName", installerPackageName);
pw.printPair("isMultiPackage", isMultiPackage);
pw.printPair("isStaged", isStaged);
+ pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
pw.println();
}
@@ -1731,6 +1749,7 @@
dest.writeString(installerPackageName);
dest.writeBoolean(isMultiPackage);
dest.writeBoolean(isStaged);
+ dest.writeLong(requiredInstalledVersionCode);
}
public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 33c0bb9..dd5ca67 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1423,6 +1423,14 @@
*/
public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120;
+ /**
+ * Installation failed return code: the required installed version code
+ * does not match the currently installed package version code.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_WRONG_INSTALLED_VERSION = -121;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
@@ -6918,6 +6926,7 @@
case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA";
case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
+ case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION";
default: return Integer.toString(status);
}
}
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index b44458a..cc5d3b1 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -419,26 +419,36 @@
Preconditions.checkNotNull(cr);
Preconditions.checkNotNull(notifyUris);
- setNotificationUris(cr, notifyUris, cr.getUserId());
+ setNotificationUris(cr, notifyUris, cr.getUserId(), true);
}
- /** @hide - set the notification uri but with an observer for a particular user's view */
- public void setNotificationUris(ContentResolver cr, List<Uri> notifyUris, int userHandle) {
+ /**
+ * Set the notification uri but with an observer for a particular user's view. Also allows
+ * disabling the use of a self observer, which is sensible if either
+ * a) the cursor's owner calls {@link #onChange(boolean)} whenever the content changes, or
+ * b) the cursor is known not to have any content observers.
+ * @hide
+ */
+ public void setNotificationUris(ContentResolver cr, List<Uri> notifyUris, int userHandle,
+ boolean registerSelfObserver) {
synchronized (mSelfObserverLock) {
mNotifyUris = notifyUris;
mNotifyUri = mNotifyUris.get(0);
mContentResolver = cr;
if (mSelfObserver != null) {
mContentResolver.unregisterContentObserver(mSelfObserver);
+ mSelfObserverRegistered = false;
}
- mSelfObserver = new SelfContentObserver(this);
- final int size = mNotifyUris.size();
- for (int i = 0; i < size; ++i) {
- final Uri notifyUri = mNotifyUris.get(i);
- mContentResolver.registerContentObserver(
- notifyUri, true, mSelfObserver, userHandle);
+ if (registerSelfObserver) {
+ mSelfObserver = new SelfContentObserver(this);
+ final int size = mNotifyUris.size();
+ for (int i = 0; i < size; ++i) {
+ final Uri notifyUri = mNotifyUris.get(i);
+ mContentResolver.registerContentObserver(
+ notifyUri, true, mSelfObserver, userHandle);
+ }
+ mSelfObserverRegistered = true;
}
- mSelfObserverRegistered = true;
}
}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 4aa6fab..099ae29 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -364,6 +364,8 @@
* count steps if it is not activated. This sensor is ideal for fitness tracking applications.
* It is defined as an {@link Sensor#REPORTING_MODE_ON_CHANGE} sensor.
* <p>
+ * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}.
+ * <p>
* See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
*/
public static final int TYPE_STEP_COUNTER = 19;
@@ -382,6 +384,8 @@
* gyroscope. This sensor uses lower power than the other rotation vectors, because it doesn't
* use the gyroscope. However, it is more noisy and will work best outdoors.
* <p>
+ * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}.
+ * <p>
* See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
*/
public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20;
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index d7db1f5..226b8e5 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -36,6 +36,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* A class for describing camera output, which contains a {@link Surface} and its specific
@@ -692,7 +693,8 @@
mIsShared != other.mIsShared ||
mConfiguredFormat != other.mConfiguredFormat ||
mConfiguredDataspace != other.mConfiguredDataspace ||
- mConfiguredGenerationId != other.mConfiguredGenerationId)
+ mConfiguredGenerationId != other.mConfiguredGenerationId ||
+ !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId))
return false;
int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 83e1980..3cdebac 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -26,12 +26,14 @@
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
+import android.util.Log;
import com.android.internal.util.Preconditions;
import libcore.io.IoUtils;
-import java.io.FileDescriptor;
+import java.io.File;
+import java.io.FileNotFoundException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
@@ -45,6 +47,9 @@
@TestApi
@SystemService(Context.BUGREPORT_SERVICE)
public final class BugreportManager {
+
+ private static final String TAG = "BugreportManager";
+
private final Context mContext;
private final IDumpstate mBinder;
@@ -149,16 +154,22 @@
Preconditions.checkNotNull(executor);
Preconditions.checkNotNull(callback);
+ if (screenshotFd == null) {
+ // Binder needs a valid File Descriptor to be passed
+ screenshotFd = ParcelFileDescriptor.open(new File("/dev/null"),
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ }
DumpstateListener dsListener = new DumpstateListener(executor, callback);
// Note: mBinder can get callingUid from the binder transaction.
mBinder.startBugreport(-1 /* callingUid */,
mContext.getOpPackageName(),
bugreportFd.getFileDescriptor(),
- (screenshotFd != null
- ? screenshotFd.getFileDescriptor() : new FileDescriptor()),
+ screenshotFd.getFileDescriptor(),
params.getMode(), dsListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (FileNotFoundException e) {
+ Log.wtf(TAG, "Not able to find /dev/null file: ", e);
} finally {
// We can close the file descriptors here because binder would have duped them.
IoUtils.closeQuietly(bugreportFd);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 75b40fd..7c5a1fb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7762,6 +7762,9 @@
*/
public static final String UI_NIGHT_MODE = "ui_night_mode";
+ private static final Validator UI_NIGHT_MODE_VALIDATOR =
+ new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+
/**
* Whether screensavers are enabled.
* @hide
@@ -8908,6 +8911,7 @@
ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
NOTIFICATION_NEW_INTERRUPTION_MODEL,
TRUST_AGENTS_EXTEND_UNLOCK,
+ UI_NIGHT_MODE,
LOCK_SCREEN_WHEN_TRUST_LOST,
SKIP_GESTURE,
SILENCE_GESTURE,
@@ -9101,6 +9105,7 @@
VALIDATORS.put(SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR);
VALIDATORS.put(ODI_CAPTIONS_ENABLED, ODI_CAPTIONS_ENABLED_VALIDATOR);
VALIDATORS.put(DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(UI_NIGHT_MODE, UI_NIGHT_MODE_VALIDATOR);
}
/**
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index d415387..83abf1a 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -173,6 +173,6 @@
}
private InputMethodManager getImm() {
- return mController.getViewRoot().mDisplayContext.getSystemService(InputMethodManager.class);
+ return mController.getViewRoot().mContext.getSystemService(InputMethodManager.class);
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 8070e76..bbb9053 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -848,6 +848,10 @@
position.width() / (float) mSurfaceWidth,
0.0f, 0.0f,
position.height() / (float) mSurfaceHeight);
+ if (mViewVisibility) {
+ mRtTransaction.show(surface);
+ }
+
}
private void setParentSpaceRectangle(Rect position, long frameNumber) {
@@ -914,27 +918,15 @@
if (mSurfaceControl == null) {
return;
}
- if (mRtHandlingPositionUpdates) {
- mRtHandlingPositionUpdates = false;
- // This callback will happen while the UI thread is blocked, so we can
- // safely access other member variables at this time.
- // So do what the UI thread would have done if RT wasn't handling position
- // updates.
- if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
- try {
- if (DEBUG) {
- Log.d(TAG, String.format("%d updateSurfacePosition, "
- + "postion = [%d, %d, %d, %d]",
- System.identityHashCode(this),
- mScreenRect.left, mScreenRect.top,
- mScreenRect.right, mScreenRect.bottom));
- }
- setParentSpaceRectangle(mScreenRect, frameNumber);
- } catch (Exception ex) {
- Log.e(TAG, "Exception configuring surface", ex);
- }
- }
+
+ if (frameNumber > 0) {
+ final ViewRootImpl viewRoot = getViewRootImpl();
+
+ mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface,
+ frameNumber);
}
+ mRtTransaction.hide(mSurfaceControl);
+ mRtTransaction.apply();
}
};
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 3bae4b8..3d3d5dc 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -177,7 +177,7 @@
* Forces smart-dark to be always on.
* @hide
*/
- public static final String DEBUG_FORCE_DARK = "persist.hwui.force_dark";
+ public static final String DEBUG_FORCE_DARK = "debug.hwui.force_dark";
public static int EGL_CONTEXT_PRIORITY_HIGH_IMG = 0x3101;
public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b813bc3..4463e13 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -283,13 +283,7 @@
@GuardedBy("mWindowCallbacks")
final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>();
@UnsupportedAppUsage
- final Context mContext;
- /**
- * TODO(b/116349163): Check if we can merge this into {@link #mContext}.
- * @hide
- */
- @NonNull
- public Context mDisplayContext;
+ public final Context mContext;
@UnsupportedAppUsage
final IWindowSession mWindowSession;
@@ -595,7 +589,6 @@
public ViewRootImpl(Context context, Display display) {
mContext = context;
- mDisplayContext = context.createDisplayContext(display);
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
@@ -1379,7 +1372,7 @@
} else {
mDisplay = preferredDisplay;
}
- mDisplayContext = mContext.createDisplayContext(mDisplay);
+ mContext.updateDisplay(mDisplay.getDisplayId());
}
void pokeDrawLockIfNeeded() {
@@ -2725,7 +2718,7 @@
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
- InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null && imTarget) {
imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
@@ -2859,7 +2852,7 @@
mLastWasImTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
- InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onPreWindowFocus(mView, hasWindowFocus);
}
@@ -4564,8 +4557,7 @@
enqueueInputEvent(event, null, 0, true);
} break;
case MSG_CHECK_FOCUS: {
- InputMethodManager imm =
- mDisplayContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null) {
imm.checkFocus();
}
@@ -5110,7 +5102,7 @@
@Override
protected int onProcess(QueuedInputEvent q) {
if (mLastWasImTarget && !isInLocalFocusMode()) {
- InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null) {
final InputEvent event = q.mEvent;
if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 2c79299..b387a68 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -16,6 +16,7 @@
package android.view.autofill;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,10 +35,10 @@
private static final int FLAG_HAS_SESSION = 0x4;
private final int mViewId;
- private final int mFlags;
+ private int mFlags;
private final int mVirtualIntId;
private final long mVirtualLongId;
- private final int mSessionId;
+ private int mSessionId;
/** @hide */
@TestApi
@@ -72,6 +73,12 @@
}
/** @hide */
+ public static AutofillId withoutSession(@NonNull AutofillId id) {
+ final int flags = id.mFlags & ~FLAG_HAS_SESSION;
+ return new AutofillId(flags, id.mViewId, id.mVirtualLongId, NO_SESSION);
+ }
+
+ /** @hide */
public int getViewId() {
return mViewId;
}
@@ -135,7 +142,8 @@
return !isVirtualInt() && !isVirtualLong();
}
- private boolean hasSession() {
+ /** @hide */
+ public boolean hasSession() {
return (mFlags & FLAG_HAS_SESSION) != 0;
}
@@ -144,6 +152,18 @@
return mSessionId;
}
+ /** @hide */
+ public void setSessionId(int sessionId) {
+ mFlags |= FLAG_HAS_SESSION;
+ mSessionId = sessionId;
+ }
+
+ /** @hide */
+ public void resetSessionId() {
+ mFlags &= ~FLAG_HAS_SESSION;
+ mSessionId = NO_SESSION;
+ }
+
/////////////////////////////////
// Object "contract" methods. //
/////////////////////////////////
@@ -172,6 +192,17 @@
return true;
}
+ /** @hide */
+ @TestApi
+ public boolean equalsIgnoreSession(@Nullable AutofillId other) {
+ if (this == other) return true;
+ if (other == null) return false;
+ if (mViewId != other.mViewId) return false;
+ if (mVirtualIntId != other.mVirtualIntId) return false;
+ if (mVirtualLongId != other.mVirtualLongId) return false;
+ return true;
+ }
+
@Override
public String toString() {
final StringBuilder builder = new StringBuilder().append(mViewId);
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 6503a80..5872d3f 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1139,6 +1139,7 @@
if (mEnteredIds == null) {
mEnteredIds = new ArraySet<>(1);
}
+ id.resetSessionId();
mEnteredIds.add(id);
}
@@ -2177,6 +2178,9 @@
private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
boolean saveOnAllViewsInvisible, boolean saveOnFinish,
@Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
+ if (saveTriggerId != null) {
+ saveTriggerId.resetSessionId();
+ }
synchronized (mLock) {
if (sVerbose) {
Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId
@@ -2202,6 +2206,7 @@
mFillableIds = new ArraySet<>(fillableIds.length);
}
for (AutofillId id : fillableIds) {
+ id.resetSessionId();
mFillableIds.add(id);
}
}
@@ -2264,6 +2269,11 @@
* session when they're entered.
*/
private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) {
+ if (autofillableIds != null) {
+ for (int i = 0; i < autofillableIds.size(); i++) {
+ autofillableIds.get(i).resetSessionId();
+ }
+ }
synchronized (mLock) {
if (sVerbose) {
Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
@@ -2879,6 +2889,7 @@
final int numIds = trackedIds.length;
for (int i = 0; i < numIds; i++) {
final AutofillId id = trackedIds[i];
+ id.resetSessionId();
if (isVisible[i]) {
mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 8460840..e3c2bd1 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -37,6 +37,7 @@
import android.util.Log;
import android.view.View;
import android.view.ViewStructure;
+import android.view.WindowManager;
import android.view.contentcapture.ContentCaptureSession.FlushReason;
import com.android.internal.annotations.GuardedBy;
@@ -343,10 +344,9 @@
/** @hide */
@UiThread
public void onActivityCreated(@NonNull IBinder applicationToken,
- @NonNull ComponentName activityComponent, int flags) {
+ @NonNull ComponentName activityComponent) {
if (mOptions.lite) return;
synchronized (mLock) {
- mFlags |= flags;
getMainContentCaptureSession().start(applicationToken, activityComponent, mFlags);
}
}
@@ -499,6 +499,32 @@
}
/**
+ * Called by apps to update flag secure when window attributes change.
+ *
+ * @hide
+ */
+ public void updateWindowAttributes(@NonNull WindowManager.LayoutParams params) {
+ if (sDebug) {
+ Log.d(TAG, "updateWindowAttributes(): window flags=" + params.flags);
+ }
+ final boolean flagSecureEnabled =
+ (params.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0;
+
+ MainContentCaptureSession mainSession;
+ synchronized (mLock) {
+ if (flagSecureEnabled) {
+ mFlags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
+ } else {
+ mFlags &= ~ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
+ }
+ mainSession = mMainSession;
+ }
+ if (mainSession != null) {
+ mainSession.setDisabled(flagSecureEnabled);
+ }
+ }
+
+ /**
* Gets whether content capture is enabled for the given user.
*
* <p>This method is typically used by the content capture service settings page, so it can
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 8673fbe..7241664 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -593,7 +593,7 @@
}
/**
- * Called by ContentCaptureManager.setContentCaptureEnabled
+ * Sets the disabled state of content capture.
*
* @return whether disabled state was changed.
*/
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index ee5b3ec..21ead4c 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -17,6 +17,7 @@
package android.view.inputmethod;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Parcel;
@@ -388,7 +389,7 @@
"required when positional parameters are specified.");
}
}
- return new CursorAnchorInfo(this);
+ return CursorAnchorInfo.create(this);
}
/**
@@ -412,30 +413,90 @@
}
}
- private CursorAnchorInfo(final Builder builder) {
- mSelectionStart = builder.mSelectionStart;
- mSelectionEnd = builder.mSelectionEnd;
- mComposingTextStart = builder.mComposingTextStart;
- mComposingText = builder.mComposingText;
- mInsertionMarkerFlags = builder.mInsertionMarkerFlags;
- mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal;
- mInsertionMarkerTop = builder.mInsertionMarkerTop;
- mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline;
- mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
- mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null ?
- builder.mCharacterBoundsArrayBuilder.build() : null;
- mMatrixValues = new float[9];
+ private static CursorAnchorInfo create(Builder builder) {
+ final SparseRectFArray characterBoundsArray =
+ builder.mCharacterBoundsArrayBuilder != null
+ ? builder.mCharacterBoundsArrayBuilder.build()
+ : null;
+ final float[] matrixValues = new float[9];
if (builder.mMatrixInitialized) {
- System.arraycopy(builder.mMatrixValues, 0, mMatrixValues, 0, 9);
+ System.arraycopy(builder.mMatrixValues, 0, matrixValues, 0, 9);
} else {
- Matrix.IDENTITY_MATRIX.getValues(mMatrixValues);
+ Matrix.IDENTITY_MATRIX.getValues(matrixValues);
}
+ return new CursorAnchorInfo(builder.mSelectionStart, builder.mSelectionEnd,
+ builder.mComposingTextStart, builder.mComposingText, builder.mInsertionMarkerFlags,
+ builder.mInsertionMarkerHorizontal, builder.mInsertionMarkerTop,
+ builder.mInsertionMarkerBaseline, builder.mInsertionMarkerBottom,
+ characterBoundsArray, matrixValues);
+ }
+
+ private CursorAnchorInfo(int selectionStart, int selectionEnd, int composingTextStart,
+ @Nullable CharSequence composingText, int insertionMarkerFlags,
+ float insertionMarkerHorizontal, float insertionMarkerTop,
+ float insertionMarkerBaseline, float insertionMarkerBottom,
+ @Nullable SparseRectFArray characterBoundsArray, @NonNull float[] matrixValues) {
+ mSelectionStart = selectionStart;
+ mSelectionEnd = selectionEnd;
+ mComposingTextStart = composingTextStart;
+ mComposingText = composingText;
+ mInsertionMarkerFlags = insertionMarkerFlags;
+ mInsertionMarkerHorizontal = insertionMarkerHorizontal;
+ mInsertionMarkerTop = insertionMarkerTop;
+ mInsertionMarkerBaseline = insertionMarkerBaseline;
+ mInsertionMarkerBottom = insertionMarkerBottom;
+ mCharacterBoundsArray = characterBoundsArray;
+ mMatrixValues = matrixValues;
+
// To keep hash function simple, we only use some complex objects for hash.
- int hash = Objects.hashCode(mComposingText);
- hash *= 31;
- hash += Arrays.hashCode(mMatrixValues);
- mHashCode = hash;
+ int hashCode = Objects.hashCode(mComposingText);
+ hashCode *= 31;
+ hashCode += Arrays.hashCode(matrixValues);
+ mHashCode = hashCode;
+ }
+
+ /**
+ * Creates a new instance of {@link CursorAnchorInfo} by applying {@code parentMatrix} to
+ * the coordinate transformation matrix.
+ *
+ * @param original {@link CursorAnchorInfo} to be cloned from.
+ * @param parentMatrix {@link Matrix} to be applied to {@code original.getMatrix()}
+ * @return A new instance of {@link CursorAnchorInfo} whose {@link CursorAnchorInfo#getMatrix()}
+ * returns {@code parentMatrix * original.getMatrix()}.
+ * @hide
+ */
+ public static CursorAnchorInfo createForAdditionalParentMatrix(CursorAnchorInfo original,
+ @NonNull Matrix parentMatrix) {
+ return new CursorAnchorInfo(original.mSelectionStart, original.mSelectionEnd,
+ original.mComposingTextStart, original.mComposingText,
+ original.mInsertionMarkerFlags, original.mInsertionMarkerHorizontal,
+ original.mInsertionMarkerTop, original.mInsertionMarkerBaseline,
+ original.mInsertionMarkerBottom, original.mCharacterBoundsArray,
+ computeMatrixValues(parentMatrix, original));
+ }
+
+ /**
+ * Returns a float array that represents {@link Matrix} elements for
+ * {@code parentMatrix * info.getMatrix()}.
+ *
+ * @param parentMatrix {@link Matrix} to be multiplied.
+ * @param info {@link CursorAnchorInfo} to provide {@link Matrix} to be multiplied.
+ * @return {@code parentMatrix * info.getMatrix()}.
+ */
+ private static float[] computeMatrixValues(@NonNull Matrix parentMatrix,
+ @NonNull CursorAnchorInfo info) {
+ if (parentMatrix.isIdentity()) {
+ return info.mMatrixValues;
+ }
+
+ final Matrix newMatrix = new Matrix();
+ newMatrix.setValues(info.mMatrixValues);
+ newMatrix.postConcat(parentMatrix);
+
+ final float[] matrixValues = new float[9];
+ newMatrix.getValues(matrixValues);
+ return matrixValues;
}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index fd73856..d302c2b 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -33,6 +33,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.os.Binder;
@@ -425,6 +426,17 @@
*/
private CursorAnchorInfo mCursorAnchorInfo = null;
+ /**
+ * A special {@link Matrix} that can be provided by the system when this instance is running
+ * inside a virtual display that is managed by {@link android.app.ActivityView}.
+ *
+ * <p>If this is non-{@code null}, {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}
+ * should be adjusted with this {@link Matrix}.</p>
+ *
+ * <p>{@code null} when not used.</p>
+ */
+ private Matrix mActivityViewToScreenMatrix = null;
+
// -----------------------------------------------------------
/**
@@ -473,6 +485,7 @@
static final int MSG_REPORT_FULLSCREEN_MODE = 10;
static final int MSG_REPORT_PRE_RENDERED = 15;
static final int MSG_APPLY_IME_VISIBILITY = 20;
+ static final int MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX = 30;
private static boolean isAutofillUIShowing(View servedView) {
AutofillManager afm = servedView.getContext().getSystemService(AutofillManager.class);
@@ -510,7 +523,7 @@
return null;
}
final InputMethodManager fallbackImm =
- viewRootImpl.mDisplayContext.getSystemService(InputMethodManager.class);
+ viewRootImpl.mContext.getSystemService(InputMethodManager.class);
if (fallbackImm == null) {
Log.e(TAG, "b/117267690: Failed to get non-null fallback IMM. view=" + view);
return null;
@@ -579,6 +592,7 @@
mCurMethod = res.method;
mCurId = res.id;
mBindSequence = res.sequence;
+ mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
}
startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
return;
@@ -686,6 +700,48 @@
}
return;
}
+ case MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX: {
+ final float[] matrixValues = (float[]) msg.obj;
+ final int bindSequence = msg.arg1;
+ synchronized (mH) {
+ if (mBindSequence != bindSequence) {
+ return;
+ }
+ if (matrixValues == null) {
+ // That this app is unbound from the parent ActivityView. In this case,
+ // calling updateCursorAnchorInfo() isn't safe. Only clear the matrix.
+ mActivityViewToScreenMatrix = null;
+ return;
+ }
+
+ final float[] currentValues = new float[9];
+ mActivityViewToScreenMatrix.getValues(currentValues);
+ if (Arrays.equals(currentValues, matrixValues)) {
+ return;
+ }
+ mActivityViewToScreenMatrix.setValues(matrixValues);
+
+ if (mCursorAnchorInfo == null || mCurMethod == null
+ || mServedInputConnectionWrapper == null) {
+ return;
+ }
+ final boolean isMonitoring = (mRequestUpdateCursorAnchorInfoMonitorMode
+ & InputConnection.CURSOR_UPDATE_MONITOR) != 0;
+ if (!isMonitoring) {
+ return;
+ }
+ // Since the host ActivityView is moved, we need to issue
+ // IMS#updateCursorAnchorInfo() again.
+ try {
+ mCurMethod.updateCursorAnchorInfo(
+ CursorAnchorInfo.createForAdditionalParentMatrix(
+ mCursorAnchorInfo, mActivityViewToScreenMatrix));
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died: " + mCurId, e);
+ }
+ }
+ return;
+ }
}
}
}
@@ -777,6 +833,11 @@
.sendToTarget();
}
+ @Override
+ public void updateActivityViewToScreenMatrix(int bindSequence, float[] matrixValues) {
+ mH.obtainMessage(MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX, bindSequence, 0,
+ matrixValues).sendToTarget();
+ }
};
final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
@@ -1192,6 +1253,7 @@
@UnsupportedAppUsage
void finishInputLocked() {
mNextServedView = null;
+ mActivityViewToScreenMatrix = null;
if (mServedView != null) {
if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView));
mServedView = null;
@@ -1668,6 +1730,7 @@
+ InputMethodDebug.startInputFlagsToString(startInputFlags));
return false;
}
+ mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
if (res.id != null) {
setInputChannelLocked(res.channel);
mBindSequence = res.sequence;
@@ -2200,7 +2263,13 @@
}
if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo);
try {
- mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
+ if (mActivityViewToScreenMatrix != null) {
+ mCurMethod.updateCursorAnchorInfo(
+ CursorAnchorInfo.createForAdditionalParentMatrix(
+ cursorAnchorInfo, mActivityViewToScreenMatrix));
+ } else {
+ mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
+ }
mCursorAnchorInfo = cursorAnchorInfo;
// Clear immediate bit (if any).
mRequestUpdateCursorAnchorInfoMonitorMode &=
@@ -2779,6 +2848,30 @@
}
/**
+ * An internal API for {@link android.app.ActivityView} to report where its embedded virtual
+ * display is placed.
+ *
+ * @param childDisplayId Display ID of the embedded virtual display.
+ * @param matrix {@link Matrix} to convert virtual display screen coordinates to
+ * the host screen coordinates. {@code null} to clear the relationship.
+ * @hide
+ */
+ public void reportActivityView(int childDisplayId, @Nullable Matrix matrix) {
+ try {
+ final float[] matrixValues;
+ if (matrix == null) {
+ matrixValues = null;
+ } else {
+ matrixValues = new float[9];
+ matrix.getValues(matrixValues);
+ }
+ mService.reportActivityView(mClient, childDisplayId, matrixValues);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Force switch to the last used input method and subtype. If the last input method didn't have
* any subtypes, the framework will simply switch to the last input method with no subtype
* specified.
@@ -2887,6 +2980,16 @@
}
}
+ /**
+ * <p>This is used for CTS test only. Do not use this method outside of CTS package.<p/>
+ * @return the ID of this display which this {@link InputMethodManager} resides
+ * @hide
+ */
+ @TestApi
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method client state for " + this + ":");
diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index fb27a2f..3fddfc8 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -26,7 +26,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
-import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
import android.util.Log;
@@ -37,6 +36,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executors;
/**
* Uses an {@link AppPredictor} to sort Resolver targets. If the AppPredictionService appears to be
@@ -46,7 +46,7 @@
class AppPredictionServiceResolverComparator extends AbstractResolverComparator {
private static final String TAG = "APSResolverComparator";
- private static final long DELAY_COMPUTE_WHEN_DEFAULTING_TO_RESOLVER_MILLIS = 200;
+ private static final boolean DEBUG = false;
private final AppPredictor mAppPredictor;
private final Context mContext;
@@ -103,23 +103,22 @@
.setTarget(target.name.getPackageName(), mUser)
.setClassName(target.name.getClassName()).build());
}
- mAppPredictor.sortTargets(appTargets, mContext.getMainExecutor(),
+ mAppPredictor.sortTargets(appTargets, Executors.newSingleThreadExecutor(),
sortedAppTargets -> {
if (sortedAppTargets.isEmpty()) {
+ if (DEBUG) {
+ Log.d(TAG, "AppPredictionService disabled. Using resolver.");
+ }
// APS for chooser is disabled. Fallback to resolver.
mResolverRankerService =
new ResolverRankerServiceResolverComparator(
mContext, mIntent, mReferrerPackage,
() -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT));
- mResolverRankerService.initRanker(mContext);
- Handler computeHandler =
- new Handler(msg -> {
- mResolverRankerService.compute(targets);
- return true;
- });
- computeHandler.sendEmptyMessageDelayed(
- 0, DELAY_COMPUTE_WHEN_DEFAULTING_TO_RESOLVER_MILLIS);
+ mResolverRankerService.compute(targets);
} else {
+ if (DEBUG) {
+ Log.d(TAG, "AppPredictionService response received");
+ }
Message msg =
Message.obtain(mHandler, RANKER_SERVICE_RESULT, sortedAppTargets);
msg.sendToTarget();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 659272c..b605eaf 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -153,8 +153,8 @@
* {@link AppPredictionManager} will be queried for direct share targets.
*/
// TODO(b/123089490): Replace with system flag
- private static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = false;
- private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = false;
+ private static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = true;
+ private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = true;
// TODO(b/123088566) Share these in a better way.
private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share";
public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share";
@@ -177,7 +177,7 @@
*/
private static final int NO_DIRECT_SHARE_ANIM_IN_MILLIS = 200;
- private static final float DIRECT_SHARE_EXPANSION_RATE = 0.7f;
+ private static final float DIRECT_SHARE_EXPANSION_RATE = 0.78f;
// TODO(b/121287224): Re-evaluate this limit
private static final int SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
@@ -2037,21 +2037,29 @@
return;
}
- int lastHeight = 0;
+ int directShareHeight = 0;
rowsToShow = Math.min(4, rowsToShow);
for (int i = 0; i < Math.min(rowsToShow, mAdapterView.getChildCount()); i++) {
- lastHeight = mAdapterView.getChildAt(i).getHeight();
- offset += lastHeight;
+ View child = mAdapterView.getChildAt(i);
+ int height = child.getHeight();
+ offset += height;
+
+ if (child.getTag() != null
+ && (child.getTag() instanceof DirectShareViewHolder)) {
+ directShareHeight = height;
+ }
}
boolean isPortrait = getResources().getConfiguration().orientation
== Configuration.ORIENTATION_PORTRAIT;
- if (lastHeight != 0 && isSendAction(getTargetIntent()) && isPortrait) {
+ if (directShareHeight != 0 && isSendAction(getTargetIntent()) && isPortrait) {
// make sure to leave room for direct share 4->8 expansion
- int expansionArea =
- (int) (mResolverDrawerLayout.getAlwaysShowHeight()
- / DIRECT_SHARE_EXPANSION_RATE);
- offset = Math.min(offset, bottom - top - lastHeight - expansionArea);
+ int requiredExpansionHeight =
+ (int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE);
+ int minHeight = bottom - top - mResolverDrawerLayout.getAlwaysShowHeight()
+ - requiredExpansionHeight;
+
+ offset = Math.min(offset, minHeight);
}
mResolverDrawerLayout.setCollapsibleHeightReserved(Math.min(offset, bottom - top));
@@ -2072,6 +2080,8 @@
private static final int MAX_SERVICE_TARGETS = 8;
+ private int mNumShortcutResults = 0;
+
// Reserve spots for incoming direct share targets by adding placeholders
private ChooserTargetInfo mPlaceHolderTargetInfo = new PlaceHolderTargetInfo();
private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
@@ -2407,9 +2417,15 @@
// This incents ChooserTargetServices to define what's truly better.
targetScore = lastScore * 0.95f;
}
- shouldNotify |= insertServiceTarget(
+ boolean isInserted = insertServiceTarget(
new SelectableTargetInfo(origTarget, target, targetScore));
+ if (isInserted && isShortcutResult) {
+ mNumShortcutResults++;
+ }
+
+ shouldNotify |= isInserted;
+
if (DEBUG) {
Log.d(TAG, " => " + target.toString() + " score=" + targetScore
+ " base=" + target.getScore()
@@ -2425,6 +2441,10 @@
}
}
+ private int getNumShortcutResults() {
+ return mNumShortcutResults;
+ }
+
/**
* Use the scoring system along with artificial boosts to create up to 3 distinct buckets:
* <ol>
@@ -2955,7 +2975,15 @@
}
public void handleScroll(View v, int y, int oldy) {
- if (mDirectShareViewHolder != null) {
+ // Only expand direct share area if there is a minimum number of shortcuts,
+ // which will help reduce the amount of visible shuffling due to older-style
+ // direct share targets.
+ int orientation = getResources().getConfiguration().orientation;
+ boolean canExpandDirectShare =
+ mChooserListAdapter.getNumShortcutResults() > getMaxTargetsPerRow()
+ && orientation == Configuration.ORIENTATION_PORTRAIT;
+
+ if (mDirectShareViewHolder != null && canExpandDirectShare) {
mDirectShareViewHolder.handleScroll(mAdapterView, y, oldy, getMaxTargetsPerRow());
}
}
diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
index d633467..01e06220 100644
--- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
@@ -315,7 +315,7 @@
}
// connect to a ranking service.
- void initRanker(Context context) {
+ private void initRanker(Context context) {
synchronized (mLock) {
if (mConnection != null && mRanker != null) {
if (DEBUG) {
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 9e8bd64..cc2caca 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -60,9 +60,11 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* A helper class for {@link android.provider.DocumentsProvider} to perform file operations on local
@@ -613,28 +615,28 @@
return projection == null ? mDefaultProjection : projection;
}
- private void startObserving(File file, Uri notifyUri) {
+ private void startObserving(File file, Uri notifyUri, DirectoryCursor cursor) {
synchronized (mObservers) {
DirectoryObserver observer = mObservers.get(file);
if (observer == null) {
- observer = new DirectoryObserver(
- file, getContext().getContentResolver(), notifyUri);
+ observer =
+ new DirectoryObserver(file, getContext().getContentResolver(), notifyUri);
observer.startWatching();
mObservers.put(file, observer);
}
- observer.mRefCount++;
+ observer.mCursors.add(cursor);
if (LOG_INOTIFY) Log.d(TAG, "after start: " + observer);
}
}
- private void stopObserving(File file) {
+ private void stopObserving(File file, DirectoryCursor cursor) {
synchronized (mObservers) {
DirectoryObserver observer = mObservers.get(file);
if (observer == null) return;
- observer.mRefCount--;
- if (observer.mRefCount == 0) {
+ observer.mCursors.remove(cursor);
+ if (observer.mCursors.size() == 0) {
mObservers.remove(file);
observer.stopWatching();
}
@@ -650,27 +652,31 @@
private final File mFile;
private final ContentResolver mResolver;
private final Uri mNotifyUri;
+ private final CopyOnWriteArrayList<DirectoryCursor> mCursors;
- private int mRefCount = 0;
-
- public DirectoryObserver(File file, ContentResolver resolver, Uri notifyUri) {
+ DirectoryObserver(File file, ContentResolver resolver, Uri notifyUri) {
super(file.getAbsolutePath(), NOTIFY_EVENTS);
mFile = file;
mResolver = resolver;
mNotifyUri = notifyUri;
+ mCursors = new CopyOnWriteArrayList<>();
}
@Override
public void onEvent(int event, String path) {
if ((event & NOTIFY_EVENTS) != 0) {
if (LOG_INOTIFY) Log.d(TAG, "onEvent() " + event + " at " + path);
+ for (DirectoryCursor cursor : mCursors) {
+ cursor.notifyChanged();
+ }
mResolver.notifyChange(mNotifyUri, null, false);
}
}
@Override
public String toString() {
- return "DirectoryObserver{file=" + mFile.getAbsolutePath() + ", ref=" + mRefCount + "}";
+ String filePath = mFile.getAbsolutePath();
+ return "DirectoryObserver{file=" + filePath + ", ref=" + mCursors.size() + "}";
}
}
@@ -681,16 +687,22 @@
super(columnNames);
final Uri notifyUri = buildNotificationUri(docId);
- setNotificationUri(getContext().getContentResolver(), notifyUri);
+ boolean registerSelfObserver = false; // Our FileObserver sees all relevant changes.
+ setNotificationUris(getContext().getContentResolver(), Arrays.asList(notifyUri),
+ getContext().getContentResolver().getUserId(), registerSelfObserver);
mFile = file;
- startObserving(mFile, notifyUri);
+ startObserving(mFile, notifyUri, this);
+ }
+
+ public void notifyChanged() {
+ onChange(false);
}
@Override
public void close() {
super.close();
- stopObserving(mFile);
+ stopObserving(mFile, this);
}
}
}
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 5d08a25..ad1ff90 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -64,6 +64,8 @@
public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5;
public static final int RECOMMEND_FAILED_INVALID_URI = -6;
public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;
+ /** {@hide} */
+ public static final int RECOMMEND_FAILED_WRONG_INSTALLED_VERSION = -8;
private static final String TAG = "PackageHelper";
// App installation location settings values
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 2cfdaaa..41f902e 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -31,4 +31,5 @@
void reportFullscreenMode(boolean fullscreen);
void reportPreRendered(in EditorInfo info);
void applyImeVisibility(boolean setVisible);
+ void updateActivityViewToScreenMatrix(int bindSequence, in float[] matrixValues);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index cb18ca1..c29e823 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -68,4 +68,7 @@
// This is kept due to @UnsupportedAppUsage.
// TODO(Bug 113914148): Consider removing this.
int getInputMethodWindowVisibleHeight();
+
+ void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
+ in float[] matrixValues);
}
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 9fe49b4..1b133d2 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -19,10 +19,12 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.graphics.Matrix;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -192,13 +194,37 @@
*/
public final int sequence;
+ @Nullable
+ private final float[] mActivityViewToScreenMatrixValues;
+
+ /**
+ * @return {@link Matrix} that corresponds to {@link #mActivityViewToScreenMatrixValues}.
+ * {@code null} if {@link #mActivityViewToScreenMatrixValues} is {@code null}.
+ */
+ @Nullable
+ public Matrix getActivityViewToScreenMatrix() {
+ if (mActivityViewToScreenMatrixValues == null) {
+ return null;
+ }
+ final Matrix matrix = new Matrix();
+ matrix.setValues(mActivityViewToScreenMatrixValues);
+ return matrix;
+ }
+
public InputBindResult(@ResultCode int _result,
- IInputMethodSession _method, InputChannel _channel, String _id, int _sequence) {
+ IInputMethodSession _method, InputChannel _channel, String _id, int _sequence,
+ @Nullable Matrix activityViewToScreenMatrix) {
result = _result;
method = _method;
channel = _channel;
id = _id;
sequence = _sequence;
+ if (activityViewToScreenMatrix == null) {
+ mActivityViewToScreenMatrixValues = null;
+ } else {
+ mActivityViewToScreenMatrixValues = new float[9];
+ activityViewToScreenMatrix.getValues(mActivityViewToScreenMatrixValues);
+ }
}
InputBindResult(Parcel source) {
@@ -211,12 +237,14 @@
}
id = source.readString();
sequence = source.readInt();
+ mActivityViewToScreenMatrixValues = source.createFloatArray();
}
@Override
public String toString() {
return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
+ " sequence=" + sequence
+ + " activityViewToScreenMatrix=" + getActivityViewToScreenMatrix()
+ "}";
}
@@ -238,6 +266,7 @@
}
dest.writeString(id);
dest.writeInt(sequence);
+ dest.writeFloatArray(mActivityViewToScreenMatrixValues);
}
/**
@@ -302,7 +331,7 @@
}
private static InputBindResult error(@ResultCode int result) {
- return new InputBindResult(result, null, null, null, -1);
+ return new InputBindResult(result, null, null, null, -1, null);
}
/**
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 3a25e67..fc2b7f6 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2027,9 +2027,7 @@
static jint android_media_AudioSystem_removeUidDeviceAffinities(JNIEnv *env, jobject clazz,
jint uid) {
-
- //###
- status_t status = NO_ERROR;//AudioSystem::removeUidDeviceAffinities();
+ status_t status = AudioSystem::removeUidDeviceAffinities((uid_t) uid);
return (jint) nativeToJavaStatus(status);
}
diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp
index cb55618..e5b72ca 100644
--- a/core/jni/android_os_HwBlob.cpp
+++ b/core/jni/android_os_HwBlob.cpp
@@ -88,7 +88,7 @@
mOwnsBuffer(true),
mHandle(0) {
if (size > 0) {
- mBuffer = malloc(size);
+ mBuffer = calloc(size, 1);
}
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 9cffb2b..1594402 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2361,4 +2361,7 @@
// OPEN: Settings > Network & internet > Mobile network > Delete sim > (answer yes to
// confirmation)
DIALOG_DELETE_SIM_PROGRESS = 1714;
+
+ // Settings > Apps and notifications > Notifications > Gentle notifications
+ GENTLE_NOTIFICATIONS_SCREEN = 1715;
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index dbd2191..e6ae226 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -159,6 +159,9 @@
optional int32 surface_size = 14 [deprecated=true];
optional string focused_app = 15;
optional AppTransitionProto app_transition = 16;
+ repeated IdentifierProto opening_apps = 17;
+ repeated IdentifierProto closing_apps = 18;
+ repeated IdentifierProto changing_apps = 19;
}
/* represents DisplayFrames */
diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_0.xml b/core/res/res/anim/ic_bluetooth_transient_animation_0.xml
new file mode 100644
index 0000000..f14cfcf
--- /dev/null
+++ b/core/res/res/anim/ic_bluetooth_transient_animation_0.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="0.5"
+ android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="233"
+ android:startOffset="17" android:valueFrom="0.5"
+ android:valueTo="0.5" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="250" android:valueFrom="0.5"
+ android:valueTo="1" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="233"
+ android:startOffset="267" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="500" android:valueFrom="1"
+ android:valueTo="0.5" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="233"
+ android:startOffset="517" android:valueFrom="0.5"
+ android:valueTo="0.5" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="750" android:valueFrom="0.5"
+ android:valueTo="1" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+</set>
diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_1.xml b/core/res/res/anim/ic_bluetooth_transient_animation_1.xml
new file mode 100644
index 0000000..934bd17
--- /dev/null
+++ b/core/res/res/anim/ic_bluetooth_transient_animation_1.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="250"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="250" android:valueFrom="1"
+ android:valueTo="0.5" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="233"
+ android:startOffset="267" android:valueFrom="0.5"
+ android:valueTo="0.5" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="500" android:valueFrom="0.5"
+ android:valueTo="1" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="233"
+ android:startOffset="517" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="750" android:valueFrom="1"
+ android:valueTo="0.5" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+</set>
diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_2.xml b/core/res/res/anim/ic_bluetooth_transient_animation_2.xml
new file mode 100644
index 0000000..7dce34c
--- /dev/null
+++ b/core/res/res/anim/ic_bluetooth_transient_animation_2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="1000"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+</set>
diff --git a/core/res/res/anim/ic_hotspot_transient_animation_0.xml b/core/res/res/anim/ic_hotspot_transient_animation_0.xml
new file mode 100644
index 0000000..d5a4c52
--- /dev/null
+++ b/core/res/res/anim/ic_hotspot_transient_animation_0.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="0.5"
+ android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="633"
+ android:startOffset="17" android:valueFrom="0.5"
+ android:valueTo="0.5" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="650" android:valueFrom="0.5"
+ android:valueTo="1" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+</set>
diff --git a/core/res/res/anim/ic_hotspot_transient_animation_1.xml b/core/res/res/anim/ic_hotspot_transient_animation_1.xml
new file mode 100644
index 0000000..36db4b9
--- /dev/null
+++ b/core/res/res/anim/ic_hotspot_transient_animation_1.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="200"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="200" android:valueFrom="1"
+ android:valueTo="0.5" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="617"
+ android:startOffset="217" android:valueFrom="0.5"
+ android:valueTo="0.5" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="833" android:valueFrom="0.5"
+ android:valueTo="1" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+</set>
diff --git a/core/res/res/anim/ic_hotspot_transient_animation_2.xml b/core/res/res/anim/ic_hotspot_transient_animation_2.xml
new file mode 100644
index 0000000..3d67436
--- /dev/null
+++ b/core/res/res/anim/ic_hotspot_transient_animation_2.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="400"
+ android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="400" android:valueFrom="1"
+ android:valueTo="0.5" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="617"
+ android:startOffset="417" android:valueFrom="0.5"
+ android:valueTo="0.5" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="1033" android:valueFrom="0.5"
+ android:valueTo="1" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator"/>
+</set>
diff --git a/core/res/res/anim/ic_hotspot_transient_animation_3.xml b/core/res/res/anim/ic_hotspot_transient_animation_3.xml
new file mode 100644
index 0000000..1495dc9
--- /dev/null
+++ b/core/res/res/anim/ic_hotspot_transient_animation_3.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="1250"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+</set>
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_0.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_0.xml
new file mode 100644
index 0000000..f71d0ee
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_0.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="scaleY" android:duration="0"
+ android:startOffset="167" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_1.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_1.xml
new file mode 100644
index 0000000..9af42a1
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="0"
+ android:startOffset="167" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml
new file mode 100644
index 0000000..0e7864a
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="scaleY" android:duration="0"
+ android:startOffset="333" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml
new file mode 100644
index 0000000..bbec629
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="0"
+ android:startOffset="333" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml
new file mode 100644
index 0000000..2c921b1
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="scaleY" android:duration="0"
+ android:startOffset="500" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml
new file mode 100644
index 0000000..51aedc0
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="0"
+ android:startOffset="500" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml
new file mode 100644
index 0000000..d1da131
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="scaleY" android:duration="0"
+ android:startOffset="667" android:valueFrom="1" android:valueTo="0"
+ android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml
new file mode 100644
index 0000000..0b6eb42
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="0"
+ android:startOffset="667" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml
new file mode 100644
index 0000000..3469d43
--- /dev/null
+++ b/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="833"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_bluetooth_transient_animation.xml b/core/res/res/drawable/ic_bluetooth_transient_animation.xml
new file mode 100644
index 0000000..61b0f1a
--- /dev/null
+++ b/core/res/res/drawable/ic_bluetooth_transient_animation.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_bluetooth_transient_animation_drawable">
+ <target android:name="_R_G_L_0_G_D_1_P_0"
+ android:animation="@anim/ic_bluetooth_transient_animation_0"/>
+ <target android:name="_R_G_L_0_G_D_2_P_0"
+ android:animation="@anim/ic_bluetooth_transient_animation_1"/>
+ <target android:name="time_group" android:animation="@anim/ic_bluetooth_transient_animation_2"/>
+</animated-vector>
diff --git a/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml
new file mode 100644
index 0000000..a520fea
--- /dev/null
+++ b/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+ android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12"
+ android:translateY="12">
+ <group android:name="_R_G_L_0_G" android:translateX="-12"
+ android:translateY="-12">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M17.71 7.71 C17.71,7.71 12,2 12,2 C12,2 11,2 11,2 C11,2 11,9.59 11,9.59 C11,9.59 6.41,5 6.41,5 C6.41,5 5,6.41 5,6.41 C5,6.41 10.59,12 10.59,12 C10.59,12 5,17.59 5,17.59 C5,17.59 6.41,19 6.41,19 C6.41,19 11,14.41 11,14.41 C11,14.41 11,22 11,22 C11,22 12,22 12,22 C12,22 17.71,16.29 17.71,16.29 C17.71,16.29 13.41,12 13.41,12 C13.41,12 17.71,7.71 17.71,7.71c M13 5.83 C13,5.83 14.88,7.71 14.88,7.71 C14.88,7.71 13,9.59 13,9.59 C13,9.59 13,5.83 13,5.83c M14.88 16.29 C14.88,16.29 13,18.17 13,18.17 C13,18.17 13,14.41 13,14.41 C13,14.41 14.88,16.29 14.88,16.29c "/>
+ <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M5 10.5 C5.83,10.5 6.5,11.17 6.5,12 C6.5,12.83 5.83,13.5 5,13.5 C4.17,13.5 3.5,12.83 3.5,12 C3.5,11.17 4.17,10.5 5,10.5c "/>
+ <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M19 10.5 C19.83,10.5 20.5,11.17 20.5,12 C20.5,12.83 19.83,13.5 19,13.5 C18.17,13.5 17.5,12.83 17.5,12 C17.5,11.17 18.17,10.5 19,10.5c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_hotspot_transient_animation.xml b/core/res/res/drawable/ic_hotspot_transient_animation.xml
new file mode 100644
index 0000000..efbdfdb
--- /dev/null
+++ b/core/res/res/drawable/ic_hotspot_transient_animation.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_hotspot_transient_animation_drawable">
+ <target android:name="_R_G_L_0_G_D_0_P_0"
+ android:animation="@anim/ic_hotspot_transient_animation_0"/>
+ <target android:name="_R_G_L_0_G_D_1_P_0"
+ android:animation="@anim/ic_hotspot_transient_animation_1"/>
+ <target android:name="_R_G_L_0_G_D_2_P_0"
+ android:animation="@anim/ic_hotspot_transient_animation_2"/>
+ <target android:name="time_group"
+ android:animation="@anim/ic_hotspot_transient_animation_3"/>
+</animated-vector>
diff --git a/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml b/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml
new file mode 100644
index 0000000..d81abbb
--- /dev/null
+++ b/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12"
+ android:translateY="12">
+ <group android:name="_R_G_L_0_G" android:translateX="-10"
+ android:translateY="-9">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M10 8 C8.9,8 8,8.9 8,10 C8,10.55 8.23,11.05 8.59,11.41 C8.95,11.77 9.45,12 10,12 C10.55,12 11.05,11.77 11.41,11.41 C11.77,11.05 12,10.55 12,10 C12,8.9 11.1,8 10,8c "/>
+ <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M10 4 C6.69,4 4,6.69 4,10 C4,11.66 4.68,13.15 5.76,14.24 C5.76,14.24 7.18,12.82 7.18,12.82 C6.45,12.1 6,11.11 6,10 C6,7.79 7.79,6 10,6 C12.21,6 14,7.79 14,10 C14,11.11 13.55,12.1 12.82,12.82 C12.82,12.82 14.24,14.24 14.24,14.24 C15.32,13.15 16,11.66 16,10 C16,6.69 13.31,4 10,4c "/>
+ <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M10 0 C4.48,0 0,4.48 0,10 C0,12.76 1.12,15.26 2.93,17.07 C2.93,17.07 4.34,15.66 4.34,15.66 C2.9,14.21 2,12.21 2,10 C2,5.58 5.58,2 10,2 C14.42,2 18,5.58 18,10 C18,12.21 17.1,14.2 15.65,15.65 C15.65,15.65 17.06,17.06 17.06,17.06 C18.88,15.26 20,12.76 20,10 C20,4.48 15.52,0 10,0c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_transient_animation.xml b/core/res/res/drawable/ic_signal_wifi_transient_animation.xml
new file mode 100644
index 0000000..0219ae5
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_transient_animation.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_signal_wifi_transient_animation_drawable">
+ <target android:name="_R_G_L_4_G_N_1_T_0"
+ android:animation="@anim/ic_signal_wifi_transient_animation_0" />
+ <target android:name="_R_G_L_3_G_N_1_T_0"
+ android:animation="@anim/ic_signal_wifi_transient_animation_1" />
+ <target android:name="_R_G_L_3_G_N_1_T_0"
+ android:animation="@anim/ic_signal_wifi_transient_animation_2" />
+ <target android:name="_R_G_L_2_G_N_1_T_0"
+ android:animation="@anim/ic_signal_wifi_transient_animation_3" />
+ <target android:name="_R_G_L_2_G_N_1_T_0"
+ android:animation="@anim/ic_signal_wifi_transient_animation_4" />
+ <target android:name="_R_G_L_1_G_N_1_T_0"
+ android:animation="@anim/ic_signal_wifi_transient_animation_5" />
+ <target android:name="_R_G_L_1_G_N_1_T_0"
+ android:animation="@anim/ic_signal_wifi_transient_animation_6" />
+ <target android:name="_R_G_L_0_G_N_1_T_0"
+ android:animation="@anim/ic_signal_wifi_transient_animation_7" />
+ <target android:name="time_group"
+ android:animation="@anim/ic_signal_wifi_transient_animation_8" />
+</animated-vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
new file mode 100644
index 0000000..9cd4925
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:width="24dp" android:viewportHeight="24" android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_4_G_N_1_T_0" android:translateX="12"
+ android:translateY="12">
+ <group android:name="_R_G_L_4_G" android:translateX="-12.25"
+ android:translateY="-10.25">
+ <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.25 2.25 C15.67,2.25 18.98,3.52 21.55,5.78 C21.55,5.78 12.25,17.1 12.25,17.1 C12.25,17.1 2.95,5.78 2.95,5.78 C5.52,3.52 8.83,2.25 12.25,2.25c M12.25 0.25 C7.5,0.25 3.22,2.33 0.25,5.64 C0.25,5.64 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.64 24.25,5.64 C21.28,2.33 17,0.25 12.25,0.25c "/>
+ </group>
+ </group>
+ <group android:name="_R_G_L_3_G_N_1_T_0" android:translateX="12"
+ android:translateY="12">
+ <group android:name="_R_G_L_3_G" android:translateX="-12.25"
+ android:translateY="-10.25">
+ <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/>
+ <path android:name="_R_G_L_3_G_D_1_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.25 11.25 C10.15,11.25 8.15,12.15 6.85,13.65 C6.85,13.65 12.25,20.25 12.25,20.25 C12.25,20.25 17.65,13.65 17.65,13.65 C16.35,12.15 14.35,11.25 12.25,11.25c "/>
+ </group>
+ </group>
+ <group android:name="_R_G_L_2_G_N_1_T_0" android:translateX="12"
+ android:translateY="12">
+ <group android:name="_R_G_L_2_G" android:translateX="-12.25"
+ android:translateY="-10.25">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/>
+ <path android:name="_R_G_L_2_G_D_1_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.25 8.25 C9.45,8.25 6.75,9.45 5.05,11.45 C5.05,11.45 12.25,20.25 12.25,20.25 C12.25,20.25 19.45,11.45 19.45,11.45 C17.75,9.45 15.05,8.25 12.25,8.25c "/>
+ </group>
+ </group>
+ <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12"
+ android:translateY="12">
+ <group android:name="_R_G_L_1_G" android:translateX="-12.25"
+ android:translateY="-10.25">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/>
+ <path android:name="_R_G_L_1_G_D_1_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.25 5.25 C8.75,5.25 5.45,6.75 3.25,9.25 C3.25,9.25 12.25,20.25 12.25,20.25 C12.25,20.25 21.25,9.25 21.25,9.25 C19.05,6.75 15.75,5.25 12.25,5.25c "/>
+ </group>
+ </group>
+ <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12"
+ android:translateY="12">
+ <group android:name="_R_G_L_0_G" android:translateX="-12.25"
+ android:translateY="-10.25">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/>
+ <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.25 5.25 C8.75,5.25 5.45,6.75 3.25,9.25 C3.25,9.25 12.25,20.25 12.25,20.25 C12.25,20.25 21.25,9.25 21.25,9.25 C19.05,6.75 15.75,5.25 12.25,5.25c "/>
+ <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/interpolator/transient_interpolator.xml b/core/res/res/interpolator/transient_interpolator.xml
new file mode 100644
index 0000000..653a8cf
--- /dev/null
+++ b/core/res/res/interpolator/transient_interpolator.xml
@@ -0,0 +1,17 @@
+<!--
+ ~ 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
+ -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
\ No newline at end of file
diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml
index 96a642c..9c725b9 100644
--- a/core/res/res/layout/chooser_grid_preview_text.xml
+++ b/core/res/res/layout/chooser_grid_preview_text.xml
@@ -23,7 +23,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:paddingBottom="@dimen/chooser_view_spacing"
android:background="?android:attr/colorBackgroundFloating">
<RelativeLayout
@@ -91,6 +90,7 @@
android:orientation="horizontal"
android:layout_marginLeft="@dimen/chooser_edge_margin_normal"
android:layout_marginRight="@dimen/chooser_edge_margin_normal"
+ android:layout_marginBottom="@dimen/chooser_view_spacing"
android:minHeight="80dp"
android:background="@drawable/chooser_content_preview_rounded"
android:id="@+id/content_preview_title_layout">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index bca0e93..f576fea 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1257,6 +1257,8 @@
<bool name="config_use_strict_phone_number_comparation">false</bool>
+ <bool name="config_use_strict_phone_number_comparation_for_russian">true</bool>
+
<!-- Display low battery warning when battery level dips to this value.
Also, the battery stats are flushed to disk when we hit this level. -->
<integer name="config_criticalBatteryWarningLevel">5</integer>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 75fd3a0..dd0a6e60 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1545,9 +1545,9 @@
<!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] -->
<string name="face_acquired_too_similar">Too similar, please change your pose.</string>
<!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] -->
- <string name="face_acquired_pan_too_extreme">Please look more directly at the screen.</string>
+ <string name="face_acquired_pan_too_extreme">Turn your head a little less.</string>
<!-- Message shown during acqusition when the user's face is tilted too high or too low [CHAR LIMIT=50] -->
- <string name="face_acquired_tilt_too_extreme">Please look more directly at the screen.</string>
+ <string name="face_acquired_tilt_too_extreme">Turn your head a little less.</string>
<!-- Message shown during acquisiton when the user's face is tilted too far left or right [CHAR LIMIT=50] -->
<string name="face_acquired_roll_too_extreme">Please straighten your head vertically.</string>
<!-- Message shown during acquisition when the user's face is obscured [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index b716ab9..b7c86d2 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -68,18 +68,27 @@
<style name="Widget.DeviceDefault.HorizontalScrollView" parent="Widget.Material.HorizontalScrollView"/>
<style name="Widget.DeviceDefault.Spinner" parent="Widget.Material.Spinner"/>
<style name="Widget.DeviceDefault.CompoundButton.Star" parent="Widget.Material.CompoundButton.Star"/>
- <style name="Widget.DeviceDefault.TabWidget" parent="Widget.Material.TabWidget"/>
+ <style name="Widget.DeviceDefault.TabWidget" parent="Widget.Material.TabWidget">
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.TabWidget</item>
+ </style>
<style name="Widget.DeviceDefault.WebTextView" parent="Widget.Material.WebTextView"/>
<style name="Widget.DeviceDefault.WebView" parent="Widget.Material.WebView"/>
- <style name="Widget.DeviceDefault.DropDownItem" parent="Widget.Material.DropDownItem"/>
- <style name="Widget.DeviceDefault.DropDownItem.Spinner" parent="Widget.Material.DropDownItem.Spinner"/>
- <style name="Widget.DeviceDefault.TextView.SpinnerItem" parent="Widget.Material.TextView.SpinnerItem"/>
+ <style name="Widget.DeviceDefault.DropDownItem" parent="Widget.Material.DropDownItem">
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.DropDownItem</item>
+ </style>
+ <style name="Widget.DeviceDefault.DropDownItem.Spinner" parent="Widget.DeviceDefault.DropDownItem"/>
+ <style name="Widget.DeviceDefault.TextView.SpinnerItem" parent="Widget.Material.TextView.SpinnerItem">
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem</item>
+ </style>
<style name="Widget.DeviceDefault.ListPopupWindow" parent="Widget.Material.ListPopupWindow"/>
<style name="Widget.DeviceDefault.PopupMenu" parent="Widget.Material.PopupMenu"/>
<style name="Widget.DeviceDefault.ActionButton" parent="Widget.Material.ActionButton"/>
<style name="Widget.DeviceDefault.ActionButton.Overflow" parent="Widget.Material.ActionButton.Overflow"/>
<style name="Widget.DeviceDefault.ActionButton.TextButton" parent="Widget.Material.ActionButton"/>
- <style name="Widget.DeviceDefault.ActionMode" parent="Widget.Material.ActionMode"/>
+ <style name="Widget.DeviceDefault.ActionMode" parent="Widget.Material.ActionMode">
+ <item name="titleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionMode.Title</item>
+ <item name="subtitleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle</item>
+ </style>
<style name="Widget.DeviceDefault.ActionButton.CloseMode" parent="Widget.Material.ActionButton.CloseMode"/>
<style name="Widget.DeviceDefault.ActionBar" parent="Widget.Material.ActionBar"/>
<style name="Widget.DeviceDefault.Button.Borderless" parent="Widget.Material.Button.Borderless"/>
@@ -108,7 +117,9 @@
<style name="Widget.DeviceDefault.AbsListView" parent="Widget.Material.AbsListView"/>
<style name="Widget.DeviceDefault.Spinner.DropDown.ActionBar" parent="Widget.Material.Spinner.DropDown.ActionBar"/>
<style name="Widget.DeviceDefault.PopupWindow.ActionMode" parent="Widget.Material.PopupWindow.ActionMode"/>
- <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Material.CompoundButton.Switch"/>
+ <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Material.CompoundButton.Switch">
+ <item name="switchTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Switch</item>
+ </style>
<style name="Widget.DeviceDefault.ExpandableListView.White" parent="Widget.Material.ExpandableListView.White"/>
<style name="Widget.DeviceDefault.FastScroll" parent="Widget.Material.FastScroll"/>
<!-- The FragmentBreadCrumbs widget is deprecated starting in API level 21 ({@link android.os.Build.VERSION_CODES#.L}). -->
@@ -146,6 +157,7 @@
<style name="Widget.DeviceDefault.TimePicker" parent="Widget.Material.TimePicker"/>
<style name="Widget.DeviceDefault.Toolbar" parent="Widget.Material.Toolbar">
<item name="titleTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Toolbar.Title</item>
+ <item name="subtitleTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Toolbar.Subtitle</item>
</style>
<style name="Widget.DeviceDefault.Light" parent="Widget.Material.Light"/>
@@ -186,12 +198,12 @@
<style name="Widget.DeviceDefault.Light.HorizontalScrollView" parent="Widget.Material.Light.HorizontalScrollView"/>
<style name="Widget.DeviceDefault.Light.Spinner" parent="Widget.Material.Light.Spinner"/>
<style name="Widget.DeviceDefault.Light.CompoundButton.Star" parent="Widget.Material.Light.CompoundButton.Star"/>
- <style name="Widget.DeviceDefault.Light.TabWidget" parent="Widget.Material.Light.TabWidget"/>
+ <style name="Widget.DeviceDefault.Light.TabWidget" parent="Widget.DeviceDefault.TabWidget"/>
<style name="Widget.DeviceDefault.Light.WebTextView" parent="Widget.Material.Light.WebTextView"/>
<style name="Widget.DeviceDefault.Light.WebView" parent="Widget.Material.Light.WebView"/>
- <style name="Widget.DeviceDefault.Light.DropDownItem" parent="Widget.Material.Light.DropDownItem"/>
- <style name="Widget.DeviceDefault.Light.DropDownItem.Spinner" parent="Widget.Material.Light.DropDownItem.Spinner"/>
- <style name="Widget.DeviceDefault.Light.TextView.SpinnerItem" parent="Widget.Material.Light.TextView.SpinnerItem"/>
+ <style name="Widget.DeviceDefault.Light.DropDownItem" parent="Widget.DeviceDefault.DropDownItem"/>
+ <style name="Widget.DeviceDefault.Light.DropDownItem.Spinner" parent="Widget.DeviceDefault.DropDownItem.Spinner"/>
+ <style name="Widget.DeviceDefault.Light.TextView.SpinnerItem" parent="Widget.DeviceDefault.TextView.SpinnerItem"/>
<style name="Widget.DeviceDefault.Light.ListPopupWindow" parent="Widget.Material.Light.ListPopupWindow"/>
<style name="Widget.DeviceDefault.Light.PopupMenu" parent="Widget.Material.Light.PopupMenu"/>
<style name="Widget.DeviceDefault.Light.Tab" parent="Widget.Material.Light.Tab"/>
@@ -202,12 +214,10 @@
<style name="Widget.DeviceDefault.Light.ActionMode" parent="Widget.Material.Light.ActionMode"/>
<style name="Widget.DeviceDefault.Light.ActionButton.CloseMode" parent="Widget.Material.Light.ActionButton.CloseMode"/>
<style name="Widget.DeviceDefault.Light.ActionBar" parent="Widget.Material.Light.ActionBar"/>
- <style name="Widget.DeviceDefault.Light.ActionBar.TabView" parent="Widget.Material.Light.ActionBar.TabView"/>
- <style name="Widget.DeviceDefault.Light.ActionBar.TabText" parent="Widget.Material.Light.ActionBar.TabText"/>
- <style name="Widget.DeviceDefault.Light.ActionBar.TabBar" parent="Widget.Material.Light.ActionBar.TabBar"/>
- <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.Material.Light.ActionBar.Solid">
- <item name="titleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionBar.Title</item>
- </style>
+ <style name="Widget.DeviceDefault.Light.ActionBar.TabView" parent="Widget.DeviceDefault.ActionBar.TabView" />
+ <style name="Widget.DeviceDefault.Light.ActionBar.TabText" parent="Widget.DeviceDefault.ActionBar.TabText" />
+ <style name="Widget.DeviceDefault.Light.ActionBar.TabBar" parent="Widget.DeviceDefault.ActionBar.TabBar" />
+ <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.DeviceDefault.ActionBar.Solid" />
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
<style name="Widget.DeviceDefault.Light.ActionBar.Solid.Inverse" parent="Widget.Holo.Light.ActionBar.Solid.Inverse"/>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
@@ -307,6 +317,9 @@
<style name="TextAppearance.DeviceDefault.Widget.EditText" parent="TextAppearance.Material.Widget.EditText">
<item name="fontFamily">@string/config_bodyFontFamily</item>
</style>
+ <style name="TextAppearance.DeviceDefault.Widget.Switch" parent="TextAppearance.Material.Widget.Switch">
+ <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+ </style>
<style name="TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored" parent="TextAppearance.DeviceDefault.Widget.Button">
<item name="textColor">@color/btn_colored_borderless_text_material</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 32bd58e..d178383 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -313,6 +313,7 @@
<java-symbol type="bool" name="config_ui_enableFadingMarquee" />
<java-symbol type="bool" name="config_enableHapticTextHandle" />
<java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
+ <java-symbol type="bool" name="config_use_strict_phone_number_comparation_for_russian" />
<java-symbol type="bool" name="config_single_volume" />
<java-symbol type="bool" name="config_voice_capable" />
<java-symbol type="bool" name="config_requireCallCapableAccountForHandle" />
@@ -1406,6 +1407,9 @@
<java-symbol type="drawable" name="ic_wifi_signal_2" />
<java-symbol type="drawable" name="ic_wifi_signal_3" />
<java-symbol type="drawable" name="ic_wifi_signal_4" />
+ <java-symbol type="drawable" name="ic_signal_wifi_transient_animation" />
+ <java-symbol type="drawable" name="ic_hotspot_transient_animation" />
+ <java-symbol type="drawable" name="ic_bluetooth_transient_animation" />
<java-symbol type="drawable" name="stat_notify_rssi_in_range" />
<java-symbol type="drawable" name="stat_sys_gps_on" />
<java-symbol type="drawable" name="stat_sys_tether_wifi" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 61fb811..e767545 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -695,7 +695,6 @@
Settings.Secure.TV_INPUT_CUSTOM_LABELS,
Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
Settings.Secure.TV_USER_SETUP_COMPLETE,
- Settings.Secure.UI_NIGHT_MODE, // candidate?
Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED,
Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS,
Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED,
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
index 2f17b32..a8ca6f0 100644
--- a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
+++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
@@ -16,7 +16,10 @@
package android.view.autofill;
+import static android.view.autofill.AutofillId.NO_SESSION;
+
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.testng.Assert.assertThrows;
@@ -33,20 +36,10 @@
@Test
public void testNonVirtual() {
final AutofillId id = new AutofillId(42);
- assertThat(id.getViewId()).isEqualTo(42);
- assertThat(id.isNonVirtual()).isTrue();
- assertThat(id.isVirtualInt()).isFalse();
- assertThat(id.isVirtualLong()).isFalse();
- assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID);
- assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID);
+ assertNonVirtual(id, 42, NO_SESSION);
final AutofillId clone = cloneThroughParcel(id);
- assertThat(clone.getViewId()).isEqualTo(42);
- assertThat(clone.isNonVirtual()).isTrue();
- assertThat(clone.isVirtualInt()).isFalse();
- assertThat(clone.isVirtualLong()).isFalse();
- assertThat(clone.getVirtualChildIntId()).isEqualTo(View.NO_ID);
- assertThat(clone.getVirtualChildLongId()).isEqualTo(View.NO_ID);
+ assertNonVirtual(clone, 42, NO_SESSION);
}
@Test
@@ -124,49 +117,174 @@
}
@Test
- public void testEqualsHashCode() {
- final AutofillId realId = new AutofillId(42);
- final AutofillId realIdSame = new AutofillId(42);
- assertThat(realId).isEqualTo(realIdSame);
- assertThat(realIdSame).isEqualTo(realId);
- assertThat(realId.hashCode()).isEqualTo(realIdSame.hashCode());
+ public void testFactoryMethod_withoutSession() {
+ final AutofillId id = new AutofillId(42);
+ id.setSessionId(108);
+ assertNonVirtual(id, 42, 108);
+ final AutofillId idWithoutSession = AutofillId.withoutSession(id);
+ assertNonVirtual(idWithoutSession, 42, NO_SESSION);
+ }
- final AutofillId realIdDifferent = new AutofillId(108);
- assertThat(realId).isNotEqualTo(realIdDifferent);
- assertThat(realIdDifferent).isNotEqualTo(realId);
+ @Test
+ public void testSetResetSession() {
+ final AutofillId id = new AutofillId(42);
+ assertNonVirtual(id, 42, NO_SESSION);
+ id.setSessionId(108);
+ assertNonVirtual(id, 42, 108);
- final AutofillId virtualId = new AutofillId(42, 1);
- final AutofillId virtualIdSame = new AutofillId(42, 1);
- assertThat(virtualId).isEqualTo(virtualIdSame);
- assertThat(virtualIdSame).isEqualTo(virtualId);
- assertThat(virtualId.hashCode()).isEqualTo(virtualIdSame.hashCode());
- assertThat(virtualId).isNotEqualTo(realId);
- assertThat(realId).isNotEqualTo(virtualId);
+ final AutofillId clone1 = cloneThroughParcel(id);
+ assertNonVirtual(clone1, 42, 108);
- final AutofillId virtualIdDifferentChild = new AutofillId(42, 2);
- assertThat(virtualIdDifferentChild).isNotEqualTo(virtualId);
- assertThat(virtualId).isNotEqualTo(virtualIdDifferentChild);
- assertThat(virtualIdDifferentChild).isNotEqualTo(realId);
- assertThat(realId).isNotEqualTo(virtualIdDifferentChild);
+ id.resetSessionId();
+ assertThat(id.getSessionId()).isEqualTo(NO_SESSION);
+ final AutofillId clone2 = cloneThroughParcel(id);
+ assertNonVirtual(clone2, 42, NO_SESSION);
+ }
- final AutofillId virtualIdDifferentParent = new AutofillId(108, 1);
- assertThat(virtualIdDifferentParent).isNotEqualTo(virtualId);
- assertThat(virtualId).isNotEqualTo(virtualIdDifferentParent);
- assertThat(virtualIdDifferentParent).isNotEqualTo(virtualIdDifferentChild);
- assertThat(virtualIdDifferentChild).isNotEqualTo(virtualIdDifferentParent);
+ @Test
+ public void testEqualsHashCode_nonVirtual_same() {
+ final AutofillId id = new AutofillId(42);
+ final AutofillId sameId = new AutofillId(42);
- final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1L, 108);
- assertThat(virtualIdDifferentSession).isNotEqualTo(virtualId);
- assertThat(virtualId).isNotEqualTo(virtualIdDifferentSession);
- assertThat(virtualIdDifferentSession).isNotEqualTo(realId);
- assertThat(realId).isNotEqualTo(virtualIdDifferentSession);
+ assertThat(id).isEqualTo(sameId);
+ assertThat(sameId).isEqualTo(id);
+ assertEqualsIgnoreSession(id, sameId);
+ assertEqualsIgnoreSession(sameId, id);
+ assertThat(id.hashCode()).isEqualTo(sameId.hashCode());
+ }
- final AutofillId sameVirtualIdDifferentSession =
- new AutofillId(new AutofillId(42), 1L, 108);
- assertThat(sameVirtualIdDifferentSession).isEqualTo(virtualIdDifferentSession);
- assertThat(virtualIdDifferentSession).isEqualTo(sameVirtualIdDifferentSession);
- assertThat(sameVirtualIdDifferentSession.hashCode())
- .isEqualTo(virtualIdDifferentSession.hashCode());
+ @Test
+ public void testEqualsHashCode_nonVirtual_other() {
+ final AutofillId id = new AutofillId(42);
+ final AutofillId otherId = new AutofillId(108);
+
+ assertThat(id).isNotEqualTo(otherId);
+ assertThat(otherId).isNotEqualTo(id);
+ assertNotEqualsIgnoreSession(id, otherId);
+ assertNotEqualsIgnoreSession(otherId, id);
+ assertThat(id.hashCode()).isNotEqualTo(otherId.hashCode());
+ }
+
+ @Test
+ public void testEqualsHashCode_virtual_same() {
+ final AutofillId id = new AutofillId(42);
+ final AutofillId virtual = new AutofillId(42, 1);
+ final AutofillId sameVirtual = new AutofillId(42, 1);
+
+ assertThat(virtual).isEqualTo(sameVirtual);
+ assertThat(sameVirtual).isEqualTo(virtual);
+ assertEqualsIgnoreSession(virtual, sameVirtual);
+ assertEqualsIgnoreSession(sameVirtual, virtual);
+ assertThat(virtual.hashCode()).isEqualTo(sameVirtual.hashCode());
+ assertThat(virtual).isNotEqualTo(id);
+ assertThat(id).isNotEqualTo(virtual);
+ assertNotEqualsIgnoreSession(id, virtual);
+ assertNotEqualsIgnoreSession(virtual, id);
+ }
+
+ @Test
+ public void testEqualsHashCode_virtual_otherChild() {
+ final AutofillId id = new AutofillId(42);
+ final AutofillId virtual = new AutofillId(42, 1);
+ final AutofillId virtualOtherChild = new AutofillId(42, 2);
+
+ assertThat(virtualOtherChild).isNotEqualTo(virtual);
+ assertThat(virtual).isNotEqualTo(virtualOtherChild);
+ assertNotEqualsIgnoreSession(virtualOtherChild, virtual);
+ assertNotEqualsIgnoreSession(virtual, virtualOtherChild);
+ assertThat(virtualOtherChild).isNotEqualTo(id);
+ assertThat(id).isNotEqualTo(virtualOtherChild);
+ assertNotEqualsIgnoreSession(virtualOtherChild, id);
+ assertNotEqualsIgnoreSession(id, virtualOtherChild);
+ }
+
+ @Test
+ public void testEqualsHashCode_virtual_otherParent() {
+ final AutofillId virtual = new AutofillId(42, 1);
+ final AutofillId virtualOtherParent = new AutofillId(108, 1);
+ final AutofillId virtualOtherChild = new AutofillId(42, 2);
+
+ assertThat(virtualOtherParent).isNotEqualTo(virtual);
+ assertThat(virtual).isNotEqualTo(virtualOtherParent);
+ assertNotEqualsIgnoreSession(virtualOtherParent, virtual);
+ assertNotEqualsIgnoreSession(virtual, virtualOtherParent);
+ assertThat(virtualOtherParent).isNotEqualTo(virtualOtherChild);
+ assertThat(virtualOtherChild).isNotEqualTo(virtualOtherParent);
+ assertNotEqualsIgnoreSession(virtualOtherParent, virtualOtherChild);
+ assertNotEqualsIgnoreSession(virtualOtherChild, virtualOtherParent);
+ }
+
+ @Test
+ public void testEqualsHashCode_virtual_otherSession() {
+ final AutofillId virtual = new AutofillId(42, 1);
+ final AutofillId virtualOtherSession = new AutofillId(42, 1);
+ virtualOtherSession.setSessionId(666);
+
+ assertThat(virtualOtherSession).isNotEqualTo(virtual);
+ assertThat(virtual).isNotEqualTo(virtualOtherSession);
+ assertEqualsIgnoreSession(virtualOtherSession, virtual);
+ assertEqualsIgnoreSession(virtual, virtualOtherSession);
+ }
+
+ @Test
+ public void testEqualsHashCode_virtual_longId_same() {
+ final AutofillId hostId = new AutofillId(42);
+ final AutofillId virtual = new AutofillId(hostId, 1L, 108);
+ final AutofillId sameVirtual = new AutofillId(hostId, 1L, 108);
+
+ assertThat(sameVirtual).isEqualTo(virtual);
+ assertThat(virtual).isEqualTo(sameVirtual);
+ assertEqualsIgnoreSession(sameVirtual, virtual);
+ assertEqualsIgnoreSession(virtual, sameVirtual);
+ assertThat(sameVirtual).isNotEqualTo(hostId);
+ assertThat(hostId).isNotEqualTo(sameVirtual);
+ assertNotEqualsIgnoreSession(sameVirtual, hostId);
+ assertNotEqualsIgnoreSession(hostId, sameVirtual);
+ }
+
+ @Test
+ public void testEqualsHashCode_virtual_longId_otherChild() {
+ final AutofillId hostId = new AutofillId(42);
+ final AutofillId virtual = new AutofillId(hostId, 1L, 108);
+ final AutofillId virtualOtherChild = new AutofillId(hostId, 2L, 108);
+
+ assertThat(virtualOtherChild).isNotEqualTo(virtual);
+ assertThat(virtual).isNotEqualTo(virtualOtherChild);
+ assertNotEqualsIgnoreSession(virtualOtherChild, virtual);
+ assertNotEqualsIgnoreSession(virtual, virtualOtherChild);
+ assertThat(virtualOtherChild).isNotEqualTo(hostId);
+ assertThat(hostId).isNotEqualTo(virtualOtherChild);
+ assertNotEqualsIgnoreSession(virtualOtherChild, hostId);
+ assertNotEqualsIgnoreSession(hostId, virtualOtherChild);
+ }
+
+ @Test
+ public void testEqualsHashCode_virtual_longId_otherParent() {
+ final AutofillId hostId = new AutofillId(42);
+ final AutofillId virtual = new AutofillId(hostId, 1L, 108);
+ final AutofillId virtualOtherParent = new AutofillId(new AutofillId(666), 1L, 108);
+ final AutofillId virtualOtherChild = new AutofillId(hostId, 2L, 108);
+
+ assertThat(virtualOtherParent).isNotEqualTo(virtual);
+ assertThat(virtual).isNotEqualTo(virtualOtherParent);
+ assertNotEqualsIgnoreSession(virtualOtherParent, virtual);
+ assertNotEqualsIgnoreSession(virtual, virtualOtherParent);
+ assertThat(virtualOtherParent).isNotEqualTo(virtualOtherChild);
+ assertThat(virtualOtherChild).isNotEqualTo(virtualOtherParent);
+ assertNotEqualsIgnoreSession(virtualOtherParent, virtualOtherChild);
+ assertNotEqualsIgnoreSession(virtualOtherChild, virtualOtherParent);
+ }
+
+ @Test
+ public void testEqualsHashCode_virtual_longId_otherSession() {
+ final AutofillId hostId = new AutofillId(42);
+ final AutofillId virtual = new AutofillId(hostId, 1L, 108);
+ final AutofillId virtualOtherSession = new AutofillId(hostId, 1L, 666);
+
+ assertThat(virtualOtherSession).isNotEqualTo(virtual);
+ assertThat(virtual).isNotEqualTo(virtualOtherSession);
+ assertEqualsIgnoreSession(virtualOtherSession, virtual);
+ assertEqualsIgnoreSession(virtual, virtualOtherSession);
}
private AutofillId cloneThroughParcel(AutofillId id) {
@@ -186,4 +304,28 @@
parcel.recycle();
}
}
+
+ private void assertEqualsIgnoreSession(AutofillId id1, AutofillId id2) {
+ assertWithMessage("id1 is null").that(id1).isNotNull();
+ assertWithMessage("id2 is null").that(id2).isNotNull();
+ assertWithMessage("%s is not equal to %s", id1, id2).that(id1.equalsIgnoreSession(id2))
+ .isTrue();
+ }
+
+ private void assertNotEqualsIgnoreSession(AutofillId id1, AutofillId id2) {
+ assertWithMessage("id1 is null").that(id1).isNotNull();
+ assertWithMessage("id2 is null").that(id2).isNotNull();
+ assertWithMessage("%s is not equal to %s", id1, id2).that(id1.equalsIgnoreSession(id2))
+ .isFalse();
+ }
+
+ private void assertNonVirtual(AutofillId id, int expectedId, int expectSessionId) {
+ assertThat(id.getViewId()).isEqualTo(expectedId);
+ assertThat(id.isNonVirtual()).isTrue();
+ assertThat(id.isVirtualInt()).isFalse();
+ assertThat(id.isVirtualLong()).isFalse();
+ assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID);
+ assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID);
+ assertThat(id.getSessionId()).isEqualTo(expectSessionId);
+ }
}
diff --git a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
index ace6611..833530d 100644
--- a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
@@ -16,19 +16,9 @@
package android.view.inputmethod;
-import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
-import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
-import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL;
-
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
import android.graphics.Matrix;
-import android.graphics.RectF;
-import android.os.Parcel;
-import android.text.TextUtils;
import android.view.inputmethod.CursorAnchorInfo.Builder;
import androidx.test.filters.SmallTest;
@@ -37,437 +27,38 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Objects;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class CursorAnchorInfoTest {
- private static final float EPSILON = 0.0000001f;
-
- private static final RectF[] MANY_BOUNDS = new RectF[] {
- new RectF(101.0f, 201.0f, 301.0f, 401.0f),
- new RectF(102.0f, 202.0f, 302.0f, 402.0f),
- new RectF(103.0f, 203.0f, 303.0f, 403.0f),
- new RectF(104.0f, 204.0f, 304.0f, 404.0f),
- new RectF(105.0f, 205.0f, 305.0f, 405.0f),
- new RectF(106.0f, 206.0f, 306.0f, 406.0f),
- new RectF(107.0f, 207.0f, 307.0f, 407.0f),
- new RectF(108.0f, 208.0f, 308.0f, 408.0f),
- new RectF(109.0f, 209.0f, 309.0f, 409.0f),
- new RectF(110.0f, 210.0f, 310.0f, 410.0f),
- new RectF(111.0f, 211.0f, 311.0f, 411.0f),
- new RectF(112.0f, 212.0f, 312.0f, 412.0f),
- new RectF(113.0f, 213.0f, 313.0f, 413.0f),
- new RectF(114.0f, 214.0f, 314.0f, 414.0f),
- new RectF(115.0f, 215.0f, 315.0f, 415.0f),
- new RectF(116.0f, 216.0f, 316.0f, 416.0f),
- new RectF(117.0f, 217.0f, 317.0f, 417.0f),
- new RectF(118.0f, 218.0f, 318.0f, 418.0f),
- new RectF(119.0f, 219.0f, 319.0f, 419.0f),
- };
- private static final int[] MANY_FLAGS_ARRAY = new int[] {
- FLAG_HAS_INVISIBLE_REGION,
- FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION,
- FLAG_HAS_VISIBLE_REGION,
- FLAG_HAS_VISIBLE_REGION,
- FLAG_HAS_VISIBLE_REGION,
- FLAG_HAS_VISIBLE_REGION,
- FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
- FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
- FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
- FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
- FLAG_HAS_VISIBLE_REGION,
- FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
- FLAG_HAS_VISIBLE_REGION,
- FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
- FLAG_HAS_VISIBLE_REGION,
- FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
- FLAG_HAS_VISIBLE_REGION,
- FLAG_HAS_INVISIBLE_REGION,
- FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
- };
-
@Test
- public void testBuilder() throws Exception {
- final int SELECTION_START = 30;
- final int SELECTION_END = 40;
- final int COMPOSING_TEXT_START = 32;
- final String COMPOSING_TEXT = "test";
- final int INSERTION_MARKER_FLAGS =
- FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
- final float INSERTION_MARKER_HORIZONTAL = 10.5f;
- final float INSERTION_MARKER_TOP = 100.1f;
- final float INSERTION_MARKER_BASELINE = 110.4f;
- final float INSERTION_MARKER_BOTOM = 111.0f;
-
- Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
- TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
-
+ public void testCreateForAdditionalParentMatrix() {
+ final Matrix originalMatrix = new Matrix();
+ originalMatrix.setTranslate(10.0f, 20.0f);
final Builder builder = new Builder();
- builder.setSelectionRange(SELECTION_START, SELECTION_END)
- .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT)
- .setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
- INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS)
- .setMatrix(TRANSFORM_MATRIX);
- for (int i = 0; i < MANY_BOUNDS.length; i++) {
- final RectF bounds = MANY_BOUNDS[i];
- final int flags = MANY_FLAGS_ARRAY[i];
- builder.addCharacterBounds(i, bounds.left, bounds.top, bounds.right, bounds.bottom,
- flags);
- }
+ builder.setMatrix(originalMatrix);
- final CursorAnchorInfo info = builder.build();
- assertEquals(SELECTION_START, info.getSelectionStart());
- assertEquals(SELECTION_END, info.getSelectionEnd());
- assertEquals(COMPOSING_TEXT_START, info.getComposingTextStart());
- assertTrue(TextUtils.equals(COMPOSING_TEXT, info.getComposingText()));
- assertEquals(INSERTION_MARKER_FLAGS, info.getInsertionMarkerFlags());
- assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal(), EPSILON);
- assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop(), EPSILON);
- assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline(), EPSILON);
- assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom(), EPSILON);
- assertEquals(TRANSFORM_MATRIX, info.getMatrix());
- for (int i = 0; i < MANY_BOUNDS.length; i++) {
- final RectF expectedBounds = MANY_BOUNDS[i];
- assertEquals(expectedBounds, info.getCharacterBounds(i));
- }
- assertNull(info.getCharacterBounds(-1));
- assertNull(info.getCharacterBounds(MANY_BOUNDS.length + 1));
- for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
- final int expectedFlags = MANY_FLAGS_ARRAY[i];
- assertEquals(expectedFlags, info.getCharacterBoundsFlags(i));
- }
- assertEquals(0, info.getCharacterBoundsFlags(-1));
- assertEquals(0, info.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
+ final CursorAnchorInfo originalInstance = builder.build();
- // Make sure that the builder can reproduce the same object.
- final CursorAnchorInfo info2 = builder.build();
- assertEquals(SELECTION_START, info2.getSelectionStart());
- assertEquals(SELECTION_END, info2.getSelectionEnd());
- assertEquals(COMPOSING_TEXT_START, info2.getComposingTextStart());
- assertTrue(TextUtils.equals(COMPOSING_TEXT, info2.getComposingText()));
- assertEquals(INSERTION_MARKER_FLAGS, info2.getInsertionMarkerFlags());
- assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal(), EPSILON);
- assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop(), EPSILON);
- assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline(), EPSILON);
- assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom(), EPSILON);
- assertEquals(TRANSFORM_MATRIX, info2.getMatrix());
- for (int i = 0; i < MANY_BOUNDS.length; i++) {
- final RectF expectedBounds = MANY_BOUNDS[i];
- assertEquals(expectedBounds, info2.getCharacterBounds(i));
- }
- assertNull(info2.getCharacterBounds(-1));
- assertNull(info2.getCharacterBounds(MANY_BOUNDS.length + 1));
- for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
- final int expectedFlags = MANY_FLAGS_ARRAY[i];
- assertEquals(expectedFlags, info2.getCharacterBoundsFlags(i));
- }
- assertEquals(0, info2.getCharacterBoundsFlags(-1));
- assertEquals(0, info2.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
- assertEquals(info, info2);
- assertEquals(info.hashCode(), info2.hashCode());
+ assertEquals(originalMatrix, originalInstance.getMatrix());
- // Make sure that object can be marshaled via {@link Parsel}.
- final CursorAnchorInfo info3 = cloneViaParcel(info2);
- assertEquals(SELECTION_START, info3.getSelectionStart());
- assertEquals(SELECTION_END, info3.getSelectionEnd());
- assertEquals(COMPOSING_TEXT_START, info3.getComposingTextStart());
- assertTrue(TextUtils.equals(COMPOSING_TEXT, info3.getComposingText()));
- assertEquals(INSERTION_MARKER_FLAGS, info3.getInsertionMarkerFlags());
- assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal(), EPSILON);
- assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop(), EPSILON);
- assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline(), EPSILON);
- assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom(), EPSILON);
- assertEquals(TRANSFORM_MATRIX, info3.getMatrix());
- for (int i = 0; i < MANY_BOUNDS.length; i++) {
- final RectF expectedBounds = MANY_BOUNDS[i];
- assertEquals(expectedBounds, info3.getCharacterBounds(i));
- }
- assertNull(info3.getCharacterBounds(-1));
- assertNull(info3.getCharacterBounds(MANY_BOUNDS.length + 1));
- for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
- final int expectedFlags = MANY_FLAGS_ARRAY[i];
- assertEquals(expectedFlags, info3.getCharacterBoundsFlags(i));
- }
- assertEquals(0, info3.getCharacterBoundsFlags(-1));
- assertEquals(0, info3.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
- assertEquals(info.hashCode(), info3.hashCode());
+ final Matrix additionalParentMatrix = new Matrix();
+ additionalParentMatrix.setTranslate(1.0f, 2.0f);
+
+ final Matrix newMatrix = new Matrix(originalMatrix);
+ newMatrix.postConcat(additionalParentMatrix);
builder.reset();
- final CursorAnchorInfo uninitializedInfo = builder.build();
- assertEquals(-1, uninitializedInfo.getSelectionStart());
- assertEquals(-1, uninitializedInfo.getSelectionEnd());
- assertEquals(-1, uninitializedInfo.getComposingTextStart());
- assertNull(uninitializedInfo.getComposingText());
- assertEquals(0, uninitializedInfo.getInsertionMarkerFlags());
- assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal(), EPSILON);
- assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop(), EPSILON);
- assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline(), EPSILON);
- assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom(), EPSILON);
- assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix());
- }
+ builder.setMatrix(newMatrix);
+ // An instance created by the standard Builder class.
+ final CursorAnchorInfo newInstanceByBuilder = builder.build();
- private static void assertNotEquals(final CursorAnchorInfo reference,
- final CursorAnchorInfo actual) {
- assertFalse(Objects.equals(reference, actual));
- }
+ // An instance created by an @hide method.
+ final CursorAnchorInfo newInstanceByMethod =
+ CursorAnchorInfo.createForAdditionalParentMatrix(
+ originalInstance, additionalParentMatrix);
- @Test
- public void testEquality() throws Exception {
- final Matrix MATRIX1 = new Matrix();
- MATRIX1.setTranslate(10.0f, 20.0f);
- final Matrix MATRIX2 = new Matrix();
- MATRIX2.setTranslate(110.0f, 120.0f);
- final Matrix NAN_MATRIX = new Matrix();
- NAN_MATRIX.setValues(new float[]{
- Float.NaN, Float.NaN, Float.NaN,
- Float.NaN, Float.NaN, Float.NaN,
- Float.NaN, Float.NaN, Float.NaN});
- final int SELECTION_START1 = 2;
- final int SELECTION_END1 = 7;
- final String COMPOSING_TEXT1 = "0123456789";
- final int COMPOSING_TEXT_START1 = 0;
- final int INSERTION_MARKER_FLAGS1 = FLAG_HAS_VISIBLE_REGION;
- final float INSERTION_MARKER_HORIZONTAL1 = 10.5f;
- final float INSERTION_MARKER_TOP1 = 100.1f;
- final float INSERTION_MARKER_BASELINE1 = 110.4f;
- final float INSERTION_MARKER_BOTOM1 = 111.0f;
- final int SELECTION_START2 = 4;
- final int SELECTION_END2 = 8;
- final String COMPOSING_TEXT2 = "9876543210";
- final int COMPOSING_TEXT_START2 = 3;
- final int INSERTION_MARKER_FLAGS2 =
- FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
- final float INSERTION_MARKER_HORIZONTAL2 = 14.5f;
- final float INSERTION_MARKER_TOP2 = 200.1f;
- final float INSERTION_MARKER_BASELINE2 = 210.4f;
- final float INSERTION_MARKER_BOTOM2 = 211.0f;
-
- // Default instance should be equal.
- assertEquals(new Builder().build(), new Builder().build());
-
- assertEquals(
- new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
- new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build());
- assertNotEquals(
- new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
- new Builder().setSelectionRange(SELECTION_START1, SELECTION_END2).build());
- assertNotEquals(
- new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
- new Builder().setSelectionRange(SELECTION_START2, SELECTION_END1).build());
- assertNotEquals(
- new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
- new Builder().setSelectionRange(SELECTION_START2, SELECTION_END2).build());
- assertEquals(
- new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
- new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build());
- assertNotEquals(
- new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
- new Builder().setComposingText(COMPOSING_TEXT_START2, COMPOSING_TEXT1).build());
- assertNotEquals(
- new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
- new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT2).build());
- assertNotEquals(
- new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
- new Builder().setComposingText(COMPOSING_TEXT_START2, COMPOSING_TEXT2).build());
-
- // For insertion marker locations, {@link Float#NaN} is treated as if it was a number.
- assertEquals(
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- Float.NaN, Float.NaN, Float.NaN, Float.NaN,
- INSERTION_MARKER_FLAGS1).build(),
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- Float.NaN, Float.NaN, Float.NaN, Float.NaN,
- INSERTION_MARKER_FLAGS1).build());
-
- // Check Matrix.
- assertEquals(
- new Builder().setMatrix(MATRIX1).build(),
- new Builder().setMatrix(MATRIX1).build());
- assertNotEquals(
- new Builder().setMatrix(MATRIX1).build(),
- new Builder().setMatrix(MATRIX2).build());
- assertNotEquals(
- new Builder().setMatrix(MATRIX1).build(),
- new Builder().setMatrix(NAN_MATRIX).build());
- // Unlike insertion marker locations, {@link Float#NaN} in the matrix is treated as just a
- // NaN as usual (NaN == NaN -> false).
- assertNotEquals(
- new Builder().setMatrix(NAN_MATRIX).build(),
- new Builder().setMatrix(NAN_MATRIX).build());
-
- assertEquals(
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build(),
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build());
- assertNotEquals(
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- Float.NaN, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build(),
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build());
- assertNotEquals(
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build(),
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build());
- assertNotEquals(
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build(),
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP2,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build());
- assertNotEquals(
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build(),
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE2, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build());
- assertNotEquals(
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build(),
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build());
- assertNotEquals(
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build(),
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM2,
- INSERTION_MARKER_FLAGS1).build());
- assertNotEquals(
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS1).build(),
- new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
- INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
- INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
- INSERTION_MARKER_FLAGS2).build());
- }
-
- @Test
- public void testMatrixIsCopied() throws Exception {
- final Matrix MATRIX1 = new Matrix();
- MATRIX1.setTranslate(10.0f, 20.0f);
- final Matrix MATRIX2 = new Matrix();
- MATRIX2.setTranslate(110.0f, 120.0f);
- final Matrix MATRIX3 = new Matrix();
- MATRIX3.setTranslate(210.0f, 220.0f);
- final Matrix matrix = new Matrix();
- final Builder builder = new Builder();
-
- matrix.set(MATRIX1);
- builder.setMatrix(matrix);
- matrix.postRotate(90.0f);
-
- final CursorAnchorInfo firstInstance = builder.build();
- assertEquals(MATRIX1, firstInstance.getMatrix());
- matrix.set(MATRIX2);
- builder.setMatrix(matrix);
- final CursorAnchorInfo secondInstance = builder.build();
- assertEquals(MATRIX1, firstInstance.getMatrix());
- assertEquals(MATRIX2, secondInstance.getMatrix());
-
- matrix.set(MATRIX3);
- assertEquals(MATRIX1, firstInstance.getMatrix());
- assertEquals(MATRIX2, secondInstance.getMatrix());
- }
-
- @Test
- public void testMatrixIsRequired() throws Exception {
- final int SELECTION_START = 30;
- final int SELECTION_END = 40;
- final int COMPOSING_TEXT_START = 32;
- final String COMPOSING_TEXT = "test";
- final int INSERTION_MARKER_FLAGS = FLAG_HAS_VISIBLE_REGION;
- final float INSERTION_MARKER_HORIZONTAL = 10.5f;
- final float INSERTION_MARKER_TOP = 100.1f;
- final float INSERTION_MARKER_BASELINE = 110.4f;
- final float INSERTION_MARKER_BOTOM = 111.0f;
- Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
- TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
-
- final Builder builder = new Builder();
- // Check twice to make sure if Builder#reset() works as expected.
- for (int repeatCount = 0; repeatCount < 2; ++repeatCount) {
- builder.setSelectionRange(SELECTION_START, SELECTION_END)
- .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT);
- try {
- // Should succeed as coordinate transformation matrix is not required if no
- // positional information is specified.
- builder.build();
- } catch (IllegalArgumentException ex) {
- assertTrue(false);
- }
-
- builder.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
- INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS);
- try {
- // Coordinate transformation matrix is required if no positional information is
- // specified.
- builder.build();
- assertTrue(false);
- } catch (IllegalArgumentException ex) {
- }
-
- builder.setMatrix(TRANSFORM_MATRIX);
- try {
- // Should succeed as coordinate transformation matrix is required.
- builder.build();
- } catch (IllegalArgumentException ex) {
- assertTrue(false);
- }
-
- builder.reset();
- }
- }
-
- @Test
- public void testBuilderAddCharacterBounds() throws Exception {
- // A negative index should be rejected.
- try {
- new Builder().addCharacterBounds(-1, 0.0f, 0.0f, 0.0f, 0.0f, FLAG_HAS_VISIBLE_REGION);
- assertTrue(false);
- } catch (IllegalArgumentException ex) {
- }
- }
-
- private static CursorAnchorInfo cloneViaParcel(final CursorAnchorInfo src) {
- Parcel parcel = null;
- try {
- parcel = Parcel.obtain();
- src.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- return new CursorAnchorInfo(parcel);
- } finally {
- if (parcel != null) {
- parcel.recycle();
- }
- }
+ assertEquals(newMatrix, newInstanceByBuilder.getMatrix());
+ assertEquals(newMatrix, newInstanceByMethod.getMatrix());
+ assertEquals(newInstanceByBuilder.hashCode(), newInstanceByMethod.hashCode());
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
new file mode 100644
index 0000000..390bb76
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.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.overlaytest;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.app.UiAutomation;
+import android.content.res.Resources;
+import android.os.ParcelFileDescriptor;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+
+class LocalOverlayManager {
+ private static final long TIMEOUT = 30;
+
+ public static void setEnabledAndWait(Executor executor, final String packageName,
+ boolean enable) throws Exception {
+ final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName;
+ if (executeShellCommand("cmd overlay list").contains(pattern)) {
+ // nothing to do, overlay already in the requested state
+ return;
+ }
+
+ final Resources res = InstrumentationRegistry.getContext().getResources();
+ final String[] oldApkPaths = res.getAssets().getApkPaths();
+ FutureTask<Boolean> task = new FutureTask<>(() -> {
+ while (true) {
+ if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) {
+ return true;
+ }
+ Thread.sleep(10);
+ }
+ });
+ executor.execute(task);
+ executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName);
+ task.get(TIMEOUT, SECONDS);
+ }
+
+ private static String executeShellCommand(final String command)
+ throws Exception {
+ final UiAutomation uiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command);
+ try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ final BufferedReader reader = new BufferedReader(
+ new InputStreamReader(in, StandardCharsets.UTF_8));
+ StringBuilder str = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ str.append(line);
+ }
+ return str.toString();
+ }
+ }
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index f86743a..fdb6bbb 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -21,13 +21,11 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.app.UiAutomation;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.LocaleList;
-import android.os.ParcelFileDescriptor;
import android.util.AttributeSet;
import android.util.Xml;
@@ -569,60 +567,4 @@
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
-
- /**
- * Executes the shell command and reads all the output to ensure the command ran and didn't
- * get stuck buffering on output.
- */
- protected static String executeShellCommand(UiAutomation automation, String command)
- throws Exception {
- final ParcelFileDescriptor pfd = automation.executeShellCommand(command);
- try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
- final BufferedReader reader = new BufferedReader(
- new InputStreamReader(in, StandardCharsets.UTF_8));
- StringBuilder str = new StringBuilder();
- String line;
- while ((line = reader.readLine()) != null) {
- str.append(line);
- }
- return str.toString();
- }
- }
-
- /**
- * Enables overlay packages and waits for a configuration change event before
- * returning, to guarantee that Resources are up-to-date.
- * @param packages the list of package names to enable.
- */
- protected static void enableOverlayPackages(String... packages) throws Exception {
- enableOverlayPackages(true, packages);
- }
-
- /**
- * Disables overlay packages and waits for a configuration change event before
- * returning, to guarantee that Resources are up-to-date.
- * @param packages the list of package names to disable.
- */
- protected static void disableOverlayPackages(String... packages) throws Exception {
- enableOverlayPackages(false, packages);
- }
-
- /**
- * Enables/disables overlay packages and waits for a configuration change event before
- * returning, to guarantee that Resources are up-to-date.
- * @param enable enables the overlays when true, disables when false.
- * @param packages the list of package names to enable/disable.
- */
- private static void enableOverlayPackages(boolean enable, String[] packages)
- throws Exception {
- final UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation()
- .getUiAutomation();
- for (final String pkg : packages) {
- executeShellCommand(uiAutomation,
- "cmd overlay " + (enable ? "enable " : "disable ") + pkg);
- }
-
- // Wait for the overlay change to propagate.
- Thread.sleep(1000);
- }
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index cd3ed9d..d28c47d 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -22,6 +22,8 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.concurrent.Executor;
+
@RunWith(JUnit4.class)
@MediumTest
public class WithMultipleOverlaysTest extends OverlayBaseTest {
@@ -31,6 +33,9 @@
@BeforeClass
public static void enableOverlay() throws Exception {
- enableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG);
+ Executor executor = (cmd) -> new Thread(cmd).start();
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true);
+ LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index c0d4281..6566ad3 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -22,6 +22,8 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.concurrent.Executor;
+
@RunWith(JUnit4.class)
@MediumTest
public class WithOverlayTest extends OverlayBaseTest {
@@ -31,7 +33,9 @@
@BeforeClass
public static void enableOverlay() throws Exception {
- disableOverlayPackages(APP_OVERLAY_TWO_PKG);
- enableOverlayPackages(APP_OVERLAY_ONE_PKG, FRAMEWORK_OVERLAY_PKG);
+ Executor executor = (cmd) -> new Thread(cmd).start();
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
+ LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index 33c7b25..48cfeab 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -22,6 +22,8 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.concurrent.Executor;
+
@RunWith(JUnit4.class)
@MediumTest
public class WithoutOverlayTest extends OverlayBaseTest {
@@ -31,6 +33,9 @@
@BeforeClass
public static void disableOverlays() throws Exception {
- disableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG);
+ Executor executor = (cmd) -> new Thread(cmd).start();
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false);
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
+ LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false);
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 9248ead..8092b1d 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -166,8 +166,6 @@
auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
int bufferCount = min_undequeued_buffers + 2 + extraBuffers;
- ALOGD("Setting buffer count to %d, min_undequeued %u, extraBuffers %u",
- bufferCount, min_undequeued_buffers, extraBuffers);
native_window_set_buffer_count(window, bufferCount);
}
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 1d55334..159cf49 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -235,8 +235,6 @@
}
void EglManager::loadConfigs() {
- ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
-
// Note: The default pixel format is RGBA_8888, when other formats are
// available, we should check the target pixel format and configure the
// attributes list properly.
@@ -246,7 +244,6 @@
// Try again without dirty regions enabled
ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
mSwapBehavior = SwapBehavior::Discard;
- ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
mEglConfig = load8BitsConfig(mEglDisplay, mSwapBehavior);
} else {
// Failed to get a valid config
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index c09bd79..56e8e85 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1659,6 +1659,7 @@
private final Object mListenerLock = new Object();
private MediaCodecInfo mCodecInfo;
private final Object mCodecInfoLock = new Object();
+ private MediaCrypto mCrypto;
private static final int EVENT_CALLBACK = 1;
private static final int EVENT_SET_CALLBACK = 2;
@@ -1858,6 +1859,7 @@
@Override
protected void finalize() {
native_finalize();
+ mCrypto = null;
}
/**
@@ -1873,6 +1875,7 @@
public final void reset() {
freeAllTrackedBuffers(); // free buffers first
native_reset();
+ mCrypto = null;
}
private native final void native_reset();
@@ -1887,6 +1890,7 @@
public final void release() {
freeAllTrackedBuffers(); // free buffers first
native_release();
+ mCrypto = null;
}
private native final void native_release();
@@ -1916,6 +1920,10 @@
* @param crypto Specify a crypto object to facilitate secure decryption
* of the media data. Pass {@code null} as {@code crypto} for
* non-secure codecs.
+ * Please note that {@link MediaCodec} does NOT take ownership
+ * of the {@link MediaCrypto} object; it is the application's
+ * responsibility to properly cleanup the {@link MediaCrypto} object
+ * when not in use.
* @param flags Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
* component as an encoder.
* @throws IllegalArgumentException if the surface has been released (or is invalid),
@@ -2000,6 +2008,7 @@
}
mHasSurface = surface != null;
+ mCrypto = crypto;
native_configure(keys, values, surface, crypto, descramblerBinder, flags);
}
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 12b41ca..07dbf93 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -951,12 +951,10 @@
return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
}
- /** Returns true if the current user makes it through the setup wizard, false otherwise. */
- private boolean getIsUserSetup() {
- return mUserSetup;
- }
-
private void setNotificationViewClipBounds(int height) {
+ if (height > mNotificationView.getHeight()) {
+ height = mNotificationView.getHeight();
+ }
Rect clipBounds = new Rect();
clipBounds.set(0, 0, mNotificationView.getWidth(), height);
// Sets the clip region on the notification list view.
@@ -980,7 +978,6 @@
}
}
- private static final int SWIPE_UP_MIN_DISTANCE = 75;
private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
private static final int SWIPE_MAX_OFF_PATH = 75;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 10360a3..93c522e 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -77,15 +77,13 @@
new ConversationActions(Collections.emptyList(), null);
private Context mContext;
- private TextClassifier mTextClassifier;
+ private TextClassificationManager mTextClassificationManager;
private AssistantSettings mSettings;
private LruCache<String, Session> mSessionCache = new LruCache<>(MAX_RESULT_ID_TO_CACHE);
SmartActionsHelper(Context context, AssistantSettings settings) {
mContext = context;
- TextClassificationManager textClassificationManager =
- mContext.getSystemService(TextClassificationManager.class);
- mTextClassifier = textClassificationManager.getTextClassifier();
+ mTextClassificationManager = mContext.getSystemService(TextClassificationManager.class);
mSettings = settings;
}
@@ -244,7 +242,7 @@
.map(ConversationAction::getType)
.toArray(String[]::new))
.build();
- mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+ getTextClassifier().onTextClassifierEvent(textClassifierEvent);
}
/**
@@ -286,7 +284,7 @@
.setTypeConfig(typeConfigBuilder.build())
.build();
ConversationActions conversationActions =
- mTextClassifier.suggestConversationActions(request);
+ getTextClassifier().suggestConversationActions(request);
reportActionsGenerated(
conversationActions.getId(), conversationActions.getConversationActions());
return conversationActions;
@@ -310,7 +308,7 @@
TextClassifierEvent.TYPE_ACTIONS_SHOWN, session.resultId)
.build();
// TODO: If possible, report which replies / actions are actually seen by user.
- mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+ getTextClassifier().onTextClassifierEvent(textClassifierEvent);
}
void onNotificationDirectReplied(String key) {
@@ -322,7 +320,7 @@
createTextClassifierEventBuilder(
TextClassifierEvent.TYPE_MANUAL_REPLY, session.resultId)
.build();
- mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+ getTextClassifier().onTextClassifierEvent(textClassifierEvent);
}
void onSuggestedReplySent(String key, CharSequence reply,
@@ -340,7 +338,7 @@
.setEntityTypes(ConversationAction.TYPE_TEXT_REPLY)
.setScores(session.repliesScores.getOrDefault(reply, 0f))
.build();
- mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+ getTextClassifier().onTextClassifierEvent(textClassifierEvent);
}
void onActionClicked(String key, Notification.Action action,
@@ -361,7 +359,7 @@
TextClassifierEvent.TYPE_SMART_ACTION, session.resultId)
.setEntityTypes(actionType)
.build();
- mTextClassifier.onTextClassifierEvent(textClassifierEvent);
+ getTextClassifier().onTextClassifierEvent(textClassifierEvent);
}
private Notification.Action createNotificationActionFromRemoteAction(
@@ -471,6 +469,10 @@
return new ArrayList<>(extractMessages);
}
+ private TextClassifier getTextClassifier() {
+ return mTextClassificationManager.getTextClassifier();
+ }
+
private static boolean arePersonsEqual(Person left, Person right) {
return Objects.equals(left.getKey(), right.getKey())
&& Objects.equals(left.getName(), right.getName())
diff --git a/packages/NetworkStack/AndroidManifestBase.xml b/packages/NetworkStack/AndroidManifestBase.xml
index d00a551..3da566f 100644
--- a/packages/NetworkStack/AndroidManifestBase.xml
+++ b/packages/NetworkStack/AndroidManifestBase.xml
@@ -24,6 +24,7 @@
android:label="NetworkStack"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true"
+ android:persistent="true"
android:usesCleartextTraffic="true">
<service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index ff34578..1d351a5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -50,6 +50,8 @@
// See mConnectAttempted
private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
+ // Some Hearing Aids (especially the 2nd device) needs more time to do service discovery
+ private static final long MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT = 15000;
private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
private final Context mContext;
@@ -223,7 +225,7 @@
// various profiles
// If UUIDs are not available yet, connect will be happen
// upon arrival of the ACTION_UUID intent.
- Log.d(TAG, "No profiles. Maybe we will connect later");
+ Log.d(TAG, "No profiles. Maybe we will connect later for device " + mDevice);
return;
}
@@ -608,10 +610,12 @@
long timeout = MAX_UUID_DELAY_FOR_AUTO_CONNECT;
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) {
timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT;
+ } else if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) {
+ timeout = MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT;
}
if (BluetoothUtils.D) {
- Log.d(TAG, "onUuidChanged: Time since last connect"
+ Log.d(TAG, "onUuidChanged: Time since last connect="
+ (SystemClock.elapsedRealtime() - mConnectAttempted));
}
diff --git a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml b/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml
deleted file mode 100644
index b5dd5c3..0000000
--- a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="233"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="233"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="233"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="233"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml b/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml
deleted file mode 100644
index 14704bf..0000000
--- a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="233"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="233"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="233"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="233"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml b/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml
deleted file mode 100644
index d465f19..0000000
--- a/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="250"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M 12.699,8.701 c 0.0,1.09767 0.0,5.48833 0.0,6.586"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- <objectAnimator
- android:duration="200"
- android:propertyName="rotation"
- android:valueFrom="-45.0"
- android:valueTo="45.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_caret_down_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml b/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml
deleted file mode 100644
index 7a38ac3..0000000
--- a/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="250"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M 11.287,8.701 c 0.0,1.09767 0.0,5.48833 0.0,6.586"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- <objectAnimator
- android:duration="200"
- android:propertyName="rotation"
- android:valueFrom="45.0"
- android:valueTo="-45.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_caret_down_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml b/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml
deleted file mode 100644
index 125a616..0000000
--- a/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="250"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M 12.699,15.287 c -0.04833,0.452 -0.04833,-7.03583 0.0,-6.586"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- <objectAnimator
- android:duration="200"
- android:propertyName="rotation"
- android:valueFrom="45.0"
- android:valueTo="-45.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_caret_up_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml b/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml
deleted file mode 100644
index 3fd4df5..0000000
--- a/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="250"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M 11.287,15.287 c 0.04883,0.452 0.04883,-7.03583 0.0,-6.586"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- <objectAnimator
- android:duration="200"
- android:propertyName="rotation"
- android:valueFrom="-45.0"
- android:valueTo="45.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_caret_up_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml
deleted file mode 100644
index 5e71847..0000000
--- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="633"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml
deleted file mode 100644
index 9081e6f..0000000
--- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="200"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="633"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml
deleted file mode 100644
index eaf5f05..0000000
--- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="400"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="633"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="0.5"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="0.5"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml b/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml
deleted file mode 100644
index 919a4dd..0000000
--- a/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="333"
- android:propertyName="pathData"
- android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="pathData"
- android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="316"
- android:propertyName="pathData"
- android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="pathData"
- android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="316"
- android:propertyName="pathData"
- android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="pathData"
- android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/linear" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:valueFrom="0.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="16"
- android:propertyName="fillAlpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/drawable/button_border_selected.xml b/packages/SystemUI/res/drawable/button_border_selected.xml
index 1e40ade..01e7099 100644
--- a/packages/SystemUI/res/drawable/button_border_selected.xml
+++ b/packages/SystemUI/res/drawable/button_border_selected.xml
@@ -19,7 +19,7 @@
<solid
android:color="@color/notification_guts_selection_bg" />
<stroke
- android:width="2dp"
+ android:width="1dp"
android:color="@color/GM2_grey_300"/>
<corners android:radius="@dimen/rect_button_radius" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/button_border_unselected.xml b/packages/SystemUI/res/drawable/button_border_unselected.xml
index 4ea3764..b9c4ced 100644
--- a/packages/SystemUI/res/drawable/button_border_unselected.xml
+++ b/packages/SystemUI/res/drawable/button_border_unselected.xml
@@ -18,7 +18,7 @@
android:shape="rectangle"
android:color="@color/notification_guts_selection_bg">
<stroke
- android:width="2dp"
+ android:width="1dp"
android:color="@color/GM2_grey_300"/>
<corners android:radius="@dimen/rect_button_radius" />
diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml b/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml
deleted file mode 100644
index 33d1fb3..0000000
--- a/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.
--->
-<vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:name="ic_bluetooth_transient"
- android:width="48dp"
- android:viewportWidth="48"
- android:height="48dp"
- android:viewportHeight="48" >
- <group
- android:name="ic_bluetooth_transient_0"
- android:translateX="23.99883"
- android:translateY="23.99839" >
- <group
- android:name="ic_bluetooth_transient_pivot"
- android:translateX="-23.99883"
- android:translateY="-23.99839" >
- <group
- android:name="ic_bluetooth_white_outlines"
- android:translateX="22.73986"
- android:translateY="23.99839" >
- <group
- android:name="ic_bluetooth_white_outlines_pivot"
- android:translateX="-12.84937"
- android:translateY="-20.4772" >
- <path
- android:name="b_shape_merged"
- android:pathData="M 17.1289978027,20.4790039062 c 0.0,0.0 7.5,-7.48100280762 7.5,-7.48100280762 c 0.81999206543,-0.819000244141 0.81999206543,-2.13899230957 0.0,-2.95999145508 c 0.0,0.0 -8.93899536133,-8.93899536133 -8.93899536133,-8.93899536133 c 0.0,0.0 -0.0610046386719,-0.0610046386719 -0.0610046386718,-0.0610046386719 c -0.844009399414,-0.788009643555 -2.16799926758,-0.74299621582 -2.95600891113,0.102005004883 c -0.359985351562,0.384994506836 -0.561996459961,0.891998291016 -0.56298828125,1.41899108887 c 0.0,0.0 0.0,12.8800048828 0.0,12.8800048828 c 0.0,0.0 -8.10000610352,-8.10000610352 -8.10000610352,-8.10000610352 c -0.81999206543,-0.81999206543 -2.12100219727,-0.81999206543 -2.9409942627,0.0 c -0.819000244141,0.819000244141 -0.819000244141,2.12001037598 0.0,2.94000244141 c 0.0,0.0 10.1799926758,10.1999969482 10.1799926758,10.1999969482 c 0.0,0.0 -10.1799926758,10.1790008545 -10.1799926758,10.1790008545 c -0.819000244141,0.820999145508 -0.819000244141,2.12100219727 0.0,2.94100952148 c 0.81999206543,0.81999206543 2.12100219727,0.81999206543 2.9409942627,0.0 c 0.0,0.0 8.10000610352,-8.1009979248 8.10000610352,-8.1009979248 c 0.0,0.0 0.0,12.9009857178 0.0,12.9009857178 c 0.0,1.14801025391 0.929992675781,2.08000183105 2.08000183105,2.08000183105 c 0.526992797852,0.0 1.03399658203,-0.199996948242 1.41999816895,-0.559997558594 c 0.0,0.0 0.0989990234375,-0.100006103516 0.0989990234375,-0.100006103516 c 0.0,0.0 8.91999816895,-8.91999816895 8.91999816895,-8.91999816895 c 0.81999206543,-0.820999145508 0.81999206543,-2.13999938965 0.0,-2.95999145508 c 0.0,0.0 -7.5,-7.46000671387 -7.5,-7.46000671387 Z M 16.0899963379,15.8190002441 c 0.0,0.0 0.0,-8.59999084473 0.0,-8.59999084473 c 0.0,0.0 4.30000305176,4.30000305176 4.30000305176,4.30000305176 c 0.0,0.0 -4.30000305176,4.29998779297 -4.30000305176,4.29998779297 Z M 16.0899963379,33.7190093994 c 0.0,0.0 0.0,-8.6009979248 0.0,-8.6009979248 c 0.0,0.0 4.30000305176,4.30099487305 4.30000305176,4.30099487305 c 0.0,0.0 -4.30000305176,4.30000305176 -4.30000305176,4.30000305176 Z"
- android:fillColor="#FFFFFFFF" />
- </group>
- </group>
- <group
- android:name="dot_left_outlines"
- android:translateX="20.6501"
- android:translateY="24.00011" >
- <group
- android:name="dot_left_outlines_pivot"
- android:translateX="-14.2"
- android:translateY="-3.55" >
- <path
- android:name="dot_left"
- android:pathData="M 5.66999816895,5.66999816895 c -1.18000793457,1.17999267578 -3.08000183105,1.17999267578 -4.24000549316,0.0 c -1.17999267578,-1.16000366211 -1.17999267578,-3.03999328613 -0.0199890136719,-4.2200012207 c 0.0,0.0 0.0199890136719,-0.0200042724609 0.0199890136719,-0.0200042724609 c 1.16000366211,-1.17999267578 3.04000854492,-1.17999267578 4.2200012207,-0.0199890136719 c 0.0,0.0 0.0200042724609,0.0199890136719 0.0200042724609,0.0199890136719 c 1.17999267578,1.17900085449 1.17999267578,3.06001281738 0.0,4.24000549316 Z"
- android:fillColor="#FFFFFFFF"
- android:fillAlpha="0.5" />
- </group>
- </group>
- <group
- android:name="dot_right_outlines"
- android:translateX="27.3501"
- android:translateY="23.99741" >
- <group
- android:name="dot_right_outlines_pivot"
- android:translateX="7.1"
- android:translateY="-3.5525" >
- <path
- android:name="dot_right"
- android:pathData="M 5.66999816895,1.43499755859 c 1.17999267578,1.18000793457 1.17999267578,3.08000183105 0.0,4.24000549316 c -1.18000793457,1.17999267578 -3.08000183105,1.17999267578 -4.24000549316,0.0 c -1.17999267578,-1.16000366211 -1.17999267578,-3.04000854492 -0.0200042724609,-4.21899414062 c 0.0,0.0 0.0200042724609,-0.0210113525391 0.0200042724609,-0.0210113525391 c 1.15299987793,-1.17098999023 3.03799438477,-1.18499755859 4.20899963379,-0.0309906005859 c 0.0,0.0 0.031005859375,0.0309906005859 0.031005859375,0.0309906005859 Z"
- android:fillColor="#FFFFFFFF" />
- </group>
- </group>
- </group>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml b/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml
deleted file mode 100644
index dc3c3e2..0000000
--- a/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.
--->
-<animated-vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_bluetooth_transient" >
- <target
- android:name="dot_left"
- android:animation="@anim/ic_bluetooth_transient_dot_left_animation" />
- <target
- android:name="dot_right"
- android:animation="@anim/ic_bluetooth_transient_dot_right_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_hotspot_transient.xml b/packages/SystemUI/res/drawable/ic_hotspot_transient.xml
deleted file mode 100644
index 22f7267..0000000
--- a/packages/SystemUI/res/drawable/ic_hotspot_transient.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:name="ic_hotspot_transient"
- android:width="48dp"
- android:viewportWidth="48"
- android:height="48dp"
- android:viewportHeight="48" >
- <group
- android:name="ic_hotspot_transient_0"
- android:translateX="24.00209"
- android:translateY="24.2354" >
- <group
- android:name="ic_hotspot_transient_pivot"
- android:translateX="-24.00209"
- android:translateY="-24.2354" >
- <group
- android:name="circle01"
- android:translateX="24.0002"
- android:translateY="25.9996" >
- <group
- android:name="circle01_pivot"
- android:translateX="-24.0002"
- android:translateY="-25.9996" >
- <path
- android:name="circle01_0"
- android:pathData="M 24.0001983643,21.9996032715 c -2.19999694824,0.0 -4.0,1.80000305176 -4.0,4.0 c 0.0,2.20100402832 1.80000305176,4.0 4.0,4.0 c 2.19999694824,0.0 4.0,-1.79899597168 4.0,-4.0 c 0.0,-2.19999694824 -1.80000305176,-4.0 -4.0,-4.0 Z"
- android:fillColor="#FFFFFFFF" />
- </group>
- </group>
- <group
- android:name="circle02"
- android:translateX="24.00071"
- android:translateY="24.74686" >
- <group
- android:name="circle02_pivot"
- android:translateX="-24.00071"
- android:translateY="-24.74686" >
- <path
- android:name="circle02_0"
- android:pathData="M 36.0001983643,25.9996032715 c -0.00299072265625,-6.62699890137 -5.37899780273,-11.9969940186 -12.0059967041,-11.9940032959 c -0.5,0.0 -0.999008178711,0.031005859375 -1.4940032959,0.0940093994141 c -5.24000549316,0.640991210938 -9.55999755859,4.81999206543 -10.3600006104,10.0399932861 c -0.639999389648,4.2799987793 0.979995727539,8.22099304199 3.83999633789,10.7799987793 c 0.960006713867,0.86100769043 2.48001098633,0.660003662109 3.1190032959,-0.460006713867 c 0.481002807617,-0.839996337891 0.281005859375,-1.87998962402 -0.438995361328,-2.51899719238 c -2.21299743652,-1.97099304199 -3.15200805664,-5.00500488281 -2.44000244141,-7.8809967041 c 0.695007324219,-2.86999511719 2.93099975586,-5.11500549316 5.79899597168,-5.81900024414 c 4.29100036621,-1.08599853516 8.65000915527,1.51100158691 9.73600769043,5.80200195312 c 0.162002563477,0.639999389648 0.244003295898,1.29699707031 0.244995117188,1.95700073242 c 0.0,2.36099243164 -1.02000427246,4.45999145508 -2.66000366211,5.91999816895 c -0.720001220703,0.660003662109 -0.939987182617,1.69999694824 -0.459991455078,2.53999328613 c 0.619995117188,1.08000183105 2.08000183105,1.38000488281 3.0,0.560012817383 c 2.61599731445,-2.26699829102 4.1190032959,-5.55801391602 4.11999511719,-9.02000427246 Z"
- android:fillColor="#FFFFFFFF" />
- </group>
- </group>
- <group
- android:name="circle03"
- android:translateX="24.00209"
- android:translateY="24.23539" >
- <group
- android:name="circle03_pivot"
- android:translateX="-24.00209"
- android:translateY="-24.23539" >
- <path
- android:name="circle03_0"
- android:pathData="M 21.6602020264,6.13960266113 c -9.24000549316,1.03999328613 -16.6999969482,8.66000366211 -17.5599975586,17.9199981689 c -0.690002441406,7.00500488281 2.36599731445,13.8540039062 8.03999328613,18.0200042725 c 0.958999633789,0.700988769531 2.32000732422,0.401000976562 2.91900634766,-0.620010375977 c 0.5,-0.860000610352 0.280990600586,-1.97898864746 -0.518997192383,-2.57998657227 c -4.56001281738,-3.38000488281 -7.30000305176,-9.09901428223 -6.32000732422,-15.3990020752 c 1.08000183105,-7.0 6.91900634766,-12.5800018311 13.9600067139,-13.3610076904 c 9.63999938965,-1.09999084473 17.8199920654,6.44000244141 17.8199920654,15.8800048828 c 0.0,5.30000305176 -2.57998657227,9.95999145508 -6.53999328613,12.8800048828 c -0.800003051758,0.600997924805 -1.02000427246,1.69999694824 -0.520004272461,2.57998657227 c 0.600006103516,1.04000854492 1.96000671387,1.32099914551 2.91999816895,0.620010375977 c 5.12100219727,-3.75500488281 8.14500427246,-9.72799682617 8.13999938965,-16.0800018311 c 0.0,-11.8200073242 -10.2599945068,-21.2400054932 -22.3399963379,-19.8600006104 Z"
- android:fillColor="#FFFFFFFF" />
- </group>
- </group>
- </group>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml b/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml
deleted file mode 100644
index 929a941..0000000
--- a/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<animated-vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_hotspot_transient" >
- <target
- android:name="circle01_0"
- android:animation="@anim/ic_hotspot_transient_circle_1_animation" />
- <target
- android:name="circle02_0"
- android:animation="@anim/ic_hotspot_transient_circle_2_animation" />
- <target
- android:name="circle03_0"
- android:animation="@anim/ic_hotspot_transient_circle_3_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml
deleted file mode 100644
index 880e1bb..0000000
--- a/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:name="ic_signal_wifi_transient"
- android:width="48dp"
- android:viewportWidth="48"
- android:height="48dp"
- android:viewportHeight="48" >
- <group
- android:name="ic_signal_wifi_4_bar_48px_2"
- android:translateX="24.25"
- android:translateY="25.73401" >
- <group
- android:name="ic_signal_wifi_4_bar_48px_2_pivot"
- android:translateX="-23.21545"
- android:translateY="-18.86649" >
- <group
- android:name="wifi_2"
- android:translateX="23.481"
- android:translateY="18.71151" >
- <path
- android:name="wifi"
- android:pathData="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:fillColor="#FFFFFFFF"
- android:fillAlpha="0.5" />
- </group>
- <group
- android:name="wifi_0"
- android:translateX="23.481"
- android:translateY="18.71151" >
- <path
- android:name="wifi_1"
- android:pathData="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z"
- android:fillColor="#FFFFFFFF"
- android:fillAlpha="0" />
- </group>
- </group>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml
deleted file mode 100644
index 9f5aaeb..0000000
--- a/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<animated-vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_signal_wifi_transient" >
- <target
- android:name="wifi_1"
- android:animation="@anim/ic_signal_wifi_transient_wifi_1_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse.xml b/packages/SystemUI/res/drawable/ic_volume_collapse.xml
deleted file mode 100644
index 3853d12..0000000
--- a/packages/SystemUI/res/drawable/ic_volume_collapse.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<!--
- Copyright (C) 2017 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.
--->
-<vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:name="ic_caret_down"
- android:width="24dp"
- android:viewportWidth="24"
- android:height="24dp"
- android:viewportHeight="24"
- android:tint="?android:attr/colorControlNormal" >
- <group
- android:name="caret___4" >
- <group
- android:name="right"
- android:translateX="11.287"
- android:translateY="8.701"
- android:rotation="45" >
- <group
- android:name="right_pivot"
- android:translateX="4.242" >
- <path
- android:name="rectangle_path_1"
- android:fillColor="#FFFFFFFF"
- android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" />
- </group>
- </group>
- <group
- android:name="left"
- android:translateX="12.699"
- android:translateY="8.701"
- android:rotation="-45" >
- <group
- android:name="left_pivot"
- android:translateX="-4.242" >
- <path
- android:name="rectangle_path_2"
- android:fillColor="#FFFFFFFF"
- android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" />
- </group>
- </group>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml
index 18c6307..101a0c8 100644
--- a/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml
@@ -13,13 +13,223 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<animated-vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_volume_collapse" >
- <target
- android:name="right"
- android:animation="@anim/ic_caret_down_right_animation" />
- <target
- android:name="left"
- android:animation="@anim/ic_caret_down_left_animation" />
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="24dp" android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12"
+ android:translateY="12">
+ <group android:name="_R_G_L_1_G_T_1" android:translateX="-0.004"
+ android:translateY="-3.608" android:rotation="0">
+ <group android:name="_R_G_L_1_G" android:translateX="-0.246"
+ android:translateY="-1.642">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12"
+ android:translateY="12">
+ <group android:name="_R_G_L_0_G_T_1" android:translateX="0"
+ android:translateY="-3.585" android:rotation="0">
+ <group android:name="_R_G_L_0_G" android:translateX="-8.25"
+ android:translateY="-1.665">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "/>
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="50"
+ android:startOffset="0"
+ android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "
+ android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="33"
+ android:startOffset="50"
+ android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "
+ android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -0.55,2.35 -0.55,2.35 C-0.55,2.35 6.84,9.66 6.84,9.66c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="83"
+ android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -0.55,2.35 -0.55,2.35 C-0.55,2.35 6.84,9.66 6.84,9.66c "
+ android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -0.87,2.12 -0.87,2.12 C-0.87,2.12 6.84,9.66 6.84,9.66c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="33"
+ android:startOffset="100"
+ android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -0.87,2.12 -0.87,2.12 C-0.87,2.12 6.84,9.66 6.84,9.66c "
+ android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.05,1.92 -1.05,1.92 C-1.05,1.92 6.84,9.66 6.84,9.66c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="133"
+ android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.05,1.92 -1.05,1.92 C-1.05,1.92 6.84,9.66 6.84,9.66c "
+ android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateXY" android:duration="250"
+ android:startOffset="0" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M -0.004,-3.608C -0.004,-2.39758332538605 -0.004,2.44358332538605 -0.004,3.654">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="200"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="-90"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="50"
+ android:startOffset="0"
+ android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+ android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.2,0.59 8.2,0.59 C8.2,0.59 9.84,1.88 9.84,1.88 C9.84,1.88 1.66,9.66 1.66,9.66c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="50"
+ android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.2,0.59 8.2,0.59 C8.2,0.59 9.84,1.88 9.84,1.88 C9.84,1.88 1.66,9.66 1.66,9.66c "
+ android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="67"
+ android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+ android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 7.45,0.93 7.45,0.93 C7.45,0.93 9.23,2.03 9.23,2.03 C9.23,2.03 1.66,9.66 1.66,9.66c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="83"
+ android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 7.45,0.93 7.45,0.93 C7.45,0.93 9.23,2.03 9.23,2.03 C9.23,2.03 1.66,9.66 1.66,9.66c "
+ android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.37,1.99 9.37,1.99 C9.37,1.99 1.66,9.66 1.66,9.66c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="33"
+ android:startOffset="100"
+ android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.37,1.99 9.37,1.99 C9.37,1.99 1.66,9.66 1.66,9.66c "
+ android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="133"
+ android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+ android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateXY" android:duration="250"
+ android:startOffset="0" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,-3.585C 0,-2.3787500476837202 0,2.4457500476837204 0,3.652">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="200"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="90"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="267"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_volume_expand.xml b/packages/SystemUI/res/drawable/ic_volume_expand.xml
deleted file mode 100644
index 79ff808..0000000
--- a/packages/SystemUI/res/drawable/ic_volume_expand.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<!--
- Copyright (C) 2017 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.
--->
-<vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:name="ic_caret_up"
- android:width="24dp"
- android:viewportWidth="24"
- android:height="24dp"
- android:viewportHeight="24"
- android:tint="?android:attr/colorControlNormal" >
- <group
- android:name="caret___4" >
- <group
- android:name="right"
- android:translateX="11.287"
- android:translateY="15.287"
- android:rotation="-45" >
- <group
- android:name="right_pivot"
- android:translateX="4.242" >
- <path
- android:name="rectangle_path_1"
- android:fillColor="#FFFFFFFF"
- android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" />
- </group>
- </group>
- <group
- android:name="left"
- android:translateX="12.699"
- android:translateY="15.287"
- android:rotation="45" >
- <group
- android:name="left_pivot"
- android:translateX="-4.242" >
- <path
- android:name="rectangle_path_2"
- android:fillColor="#FFFFFFFF"
- android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" />
- </group>
- </group>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml
index abd6678..152d26c 100644
--- a/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml
@@ -13,13 +13,233 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<animated-vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_volume_expand" >
- <target
- android:name="right"
- android:animation="@anim/ic_caret_up_right_animation" />
- <target
- android:name="left"
- android:animation="@anim/ic_caret_up_left_animation" />
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="24dp" android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12"
+ android:translateY="12">
+ <group android:name="_R_G_L_1_G_T_1" android:translateX="0"
+ android:translateY="3.439" android:rotation="0">
+ <group android:name="_R_G_L_1_G" android:translateX="-8.25"
+ android:translateY="-8.1">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 8.25,9.66 8.25,9.66 C8.25,9.66 8.25,6.83 8.25,6.83 C8.25,6.83 1.66,0.25 1.66,0.25c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12"
+ android:translateY="12">
+ <group android:name="_R_G_L_0_G_T_1" android:translateX="0"
+ android:translateY="3.439" android:rotation="0">
+ <group android:name="_R_G_L_0_G" android:translateX="-0.25"
+ android:translateY="-8.1">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.25,9.66 0.25,9.66 C0.25,9.66 0.25,6.83 0.25,6.83 C0.25,6.83 6.84,0.25 6.84,0.25c "/>
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="33"
+ android:startOffset="0"
+ android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 8.25,9.66 8.25,9.66 C8.25,9.66 8.25,6.83 8.25,6.83 C8.25,6.83 1.66,0.25 1.66,0.25c "
+ android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.97,9.5 7.97,9.5 C7.97,9.5 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="33"
+ android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.97,9.5 7.97,9.5 C7.97,9.5 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c "
+ android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.71,9.4 7.71,9.4 C7.71,9.4 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="50"
+ android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.71,9.4 7.71,9.4 C7.71,9.4 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c "
+ android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 8.87,7.47 8.87,7.47 C8.87,7.47 1.66,0.25 1.66,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="67"
+ android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 8.87,7.47 8.87,7.47 C8.87,7.47 1.66,0.25 1.66,0.25c "
+ android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9,7.68 9,7.68 C9,7.68 1.66,0.25 1.66,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="83"
+ android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9,7.68 9,7.68 C9,7.68 1.66,0.25 1.66,0.25c "
+ android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9.08,7.81 9.08,7.81 C9.08,7.81 1.66,0.25 1.66,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="50"
+ android:startOffset="100"
+ android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9.08,7.81 9.08,7.81 C9.08,7.81 1.66,0.25 1.66,0.25c "
+ android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.95,9.61 7.95,9.61 C7.95,9.61 9.58,8.01 9.58,8.01 C9.58,8.01 1.66,0.25 1.66,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateXY" android:duration="250"
+ android:startOffset="0" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,3.439C 0,2.24316667461395 0,-2.54016667461395 0,-3.736">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="200"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="-90"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="0"
+ android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.24,9.63 0.24,9.63 C0.24,9.63 -0.15,7.2 -0.15,7.2 C-0.15,7.2 6.84,0.25 6.84,0.25c "
+ android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.22,9.59 0.22,9.59 C0.22,9.59 -0.4,7.45 -0.4,7.45 C-0.4,7.45 6.84,0.25 6.84,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="17"
+ android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.22,9.59 0.22,9.59 C0.22,9.59 -0.4,7.45 -0.4,7.45 C-0.4,7.45 6.84,0.25 6.84,0.25c "
+ android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.36,9.46 0.36,9.46 C0.36,9.46 -0.44,7.6 -0.44,7.6 C-0.44,7.6 6.84,0.25 6.84,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="33"
+ android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.36,9.46 0.36,9.46 C0.36,9.46 -0.44,7.6 -0.44,7.6 C-0.44,7.6 6.84,0.25 6.84,0.25c "
+ android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.71,9.4 0.71,9.4 C0.71,9.4 -0.61,7.63 -0.61,7.63 C-0.61,7.63 6.84,0.25 6.84,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="33"
+ android:startOffset="50"
+ android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.71,9.4 0.71,9.4 C0.71,9.4 -0.61,7.63 -0.61,7.63 C-0.61,7.63 6.84,0.25 6.84,0.25c "
+ android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.93,9.29 0.93,9.29 C0.93,9.29 -0.5,7.69 -0.5,7.69 C-0.5,7.69 6.84,0.25 6.84,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="17"
+ android:startOffset="83"
+ android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.93,9.29 0.93,9.29 C0.93,9.29 -0.5,7.69 -0.5,7.69 C-0.5,7.69 6.84,0.25 6.84,0.25c "
+ android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.84,9.33 0.84,9.33 C0.84,9.33 -0.53,7.8 -0.53,7.8 C-0.53,7.8 6.84,0.25 6.84,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData" android:duration="117"
+ android:startOffset="100"
+ android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.84,9.33 0.84,9.33 C0.84,9.33 -0.53,7.8 -0.53,7.8 C-0.53,7.8 6.84,0.25 6.84,0.25c "
+ android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.56,9.63 0.56,9.63 C0.56,9.63 -1.08,8.07 -1.08,8.07 C-1.08,8.07 6.84,0.25 6.84,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateXY" android:duration="250"
+ android:startOffset="0" android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 0,3.439C 0,2.24316667461395 0,-2.54016667461395 0,-3.736">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="200"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="90"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="267"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
</animated-vector>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index f9bf47b..89d1a19 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -37,16 +37,17 @@
<color name="notification_ripple_untinted_color">#30ffffff</color>
<!-- The "inside" of a notification, reached via longpress -->
- <color name="notification_guts_bg_color">@*android:color/notification_material_background_color</color>
+ <color name="notification_guts_bg_color">@color/GM2_grey_900</color>
<!-- The color of the text inside a notification -->
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
- <color name="notification_guts_selection_bg">#202124</color>
- <color name="notification_guts_selection_border">#669DF6</color>
- <color name="notification_guts_link_icon_tint">@color/GM2_grey_200</color>
+ <color name="notification_guts_selection_bg">@color/GM2_grey_800</color>
+ <color name="notification_guts_selection_border">@color/GM2_grey_700</color>
+ <color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
<color name="notification_guts_sub_text_color">@color/GM2_grey_200</color>
- <color name="notification_guts_header_text_color">@color/GM2_grey_100</color>
+ <color name="notification_guts_header_text_color">@color/GM2_grey_200</color>
+ <color name="notification_guts_button_color">@color/GM2_blue_200</color>
<!-- The color of the background in the top part of QSCustomizer -->
<color name="qs_customize_background">@color/GM2_grey_900</color>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 4e1a7d0..b673e52 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -95,6 +95,7 @@
<color name="notification_guts_header_text_color">@color/GM2_grey_900</color>
<color name="notification_silence_color">#FF32c1de</color>
<color name="notification_alert_color">#FFF87B2B</color>
+ <color name="notification_guts_button_color">@color/GM2_blue_700</color>
<color name="assist_orb_color">#ffffff</color>
@@ -177,6 +178,9 @@
<color name="GM2_red_300">#F28B82</color>
<color name="GM2_red_500">#B71C1C</color>
+ <color name="GM2_blue_200">#AECBFA</color>
+ <color name="GM2_blue_700">#1967D2</color>
+
<color name="GM2_yellow_500">#FFFBBC04</color>
<color name="GM2_green_500">#FF34A853</color>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 501b1b5..f75f255 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -108,6 +108,8 @@
<item type="id" name="display_cutout" />
+ <item type="id" name="row_tag_for_content_view" />
+
<!-- Optional cancel button on Keyguard -->
<item type="id" name="cancel_button"/>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 23de2ac..aa89dce 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -452,7 +452,7 @@
<style name="TextAppearance.NotificationInfo.Button">
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
<item name="android:textSize">16sp</item>
- <item name="android:textColor">?android:attr/colorAccent</item>
+ <item name="android:textColor">@color/notification_guts_button_color</item>
<item name="android:background">@drawable/btn_borderless_rect</item>
<item name="android:gravity">center_vertical</item>
<item name="android:focusable">true</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 40d98c1..f384507 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -151,19 +151,34 @@
return plugins.size() != 0;
}
- private void disable(PluginInfo info,
- @PluginEnabler.DisableReason int reason) {
+ private boolean isPluginWhitelisted(ComponentName pluginName) {
+ for (String componentNameOrPackage : mWhitelistedPlugins) {
+ ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage);
+ if (componentName == null) {
+ if (componentNameOrPackage.equals(pluginName.getPackageName())) {
+ return true;
+ }
+ } else {
+ if (componentName.equals(pluginName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void disable(PluginInfo info, @PluginEnabler.DisableReason int reason) {
// Live by the sword, die by the sword.
// Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
+ ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass);
// If a plugin is detected in the stack of a crash then this will be called for that
// plugin, if the plugin causing a crash cannot be identified, they are all disabled
// assuming one of them must be bad.
- if (mWhitelistedPlugins.contains(info.mPackage)) {
+ if (isPluginWhitelisted(pluginComponent)) {
// Don't disable whitelisted plugins as they are a part of the OS.
return;
}
- ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass);
Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString());
mManager.getPluginEnabler().setDisabled(pluginComponent, reason);
}
@@ -288,6 +303,13 @@
if (result.size() > 1 && !mAllowMultiple) {
// TODO: Show warning.
Log.w(TAG, "Multiple plugins found for " + mAction);
+ if (DEBUG) {
+ for (ResolveInfo info : result) {
+ ComponentName name = new ComponentName(info.serviceInfo.packageName,
+ info.serviceInfo.name);
+ Log.w(TAG, " " + name);
+ }
+ }
return;
}
for (ResolveInfo info : result) {
@@ -305,7 +327,7 @@
protected PluginInfo<T> handleLoadPlugin(ComponentName component) {
// This was already checked, but do it again here to make extra extra sure, we don't
// use these on production builds.
- if (!isDebuggable && !mWhitelistedPlugins.contains(component.getPackageName())) {
+ if (!isDebuggable && !isPluginWhitelisted(component)) {
// Never ever ever allow these on production builds, they are only for prototyping.
Log.w(TAG, "Plugin cannot be loaded on production build: " + component);
return null;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 6e3eb7c..d250acc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -70,6 +70,15 @@
public void removeListener(TaskStackChangeListener listener) {
mTaskStackListeners.remove(listener);
+ if (mTaskStackListeners.isEmpty() && mRegistered) {
+ // Unregister mTaskStackListener once we have no more listeners
+ try {
+ ActivityTaskManager.getService().unregisterTaskStackListener(this);
+ mRegistered = false;
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
+ }
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 07c2f10..9f4c403e 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -32,7 +32,6 @@
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Observer;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManager.DockEventListener;
@@ -89,6 +88,7 @@
private final Observer<Integer> mCurrentUserObserver = (newUserId) -> reload();
private final PluginManager mPluginManager;
+ @Nullable private final DockManager mDockManager;
/**
* Observe changes to dock state to know when to switch the clock face.
@@ -102,7 +102,6 @@
reload();
}
};
- @Nullable private DockManager mDockManager;
/**
* When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face
@@ -125,21 +124,24 @@
@Inject
public ClockManager(Context context, InjectionInflationController injectionInflater,
- PluginManager pluginManager, SysuiColorExtractor colorExtractor) {
+ PluginManager pluginManager, SysuiColorExtractor colorExtractor,
+ @Nullable DockManager dockManager) {
this(context, injectionInflater, pluginManager, colorExtractor,
context.getContentResolver(), new CurrentUserObservable(context),
- new SettingsWrapper(context.getContentResolver()));
+ new SettingsWrapper(context.getContentResolver()), dockManager);
}
+ @VisibleForTesting
ClockManager(Context context, InjectionInflationController injectionInflater,
PluginManager pluginManager, SysuiColorExtractor colorExtractor,
ContentResolver contentResolver, CurrentUserObservable currentUserObservable,
- SettingsWrapper settingsWrapper) {
+ SettingsWrapper settingsWrapper, DockManager dockManager) {
mContext = context;
mPluginManager = pluginManager;
mContentResolver = contentResolver;
mSettingsWrapper = settingsWrapper;
mCurrentUserObservable = currentUserObservable;
+ mDockManager = dockManager;
mPreviewClocks = new AvailableClocks();
Resources res = context.getResources();
@@ -223,9 +225,6 @@
Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE),
false, mContentObserver, UserHandle.USER_ALL);
mCurrentUserObservable.getCurrentUser().observeForever(mCurrentUserObserver);
- if (mDockManager == null) {
- mDockManager = SysUiServiceProvider.getComponent(mContext, DockManager.class);
- }
if (mDockManager != null) {
mDockManager.addListener(mDockEventListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index d071363..9ecc6f4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,6 +16,10 @@
package com.android.systemui.bubbles;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.View.INVISIBLE;
@@ -53,13 +57,13 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
@@ -210,6 +214,7 @@
mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
+ mNotificationEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
mStatusBarWindowController = statusBarWindowController;
mStatusBarStateListener = new StatusBarStateListener();
@@ -389,6 +394,46 @@
}
@SuppressWarnings("FieldCanBeLocal")
+ private final NotificationRemoveInterceptor mRemoveInterceptor =
+ new NotificationRemoveInterceptor() {
+ @Override
+ public boolean onNotificationRemoveRequested(String key, int reason) {
+ if (!mBubbleData.hasBubbleWithKey(key)) {
+ return false;
+ }
+ NotificationEntry entry = mBubbleData.getBubbleWithKey(key).entry;
+
+ final boolean isClearAll = reason == REASON_CANCEL_ALL;
+ final boolean isUserDimiss = reason == REASON_CANCEL;
+ final boolean isAppCancel = reason == REASON_APP_CANCEL
+ || reason == REASON_APP_CANCEL_ALL;
+
+ // Need to check for !appCancel here because the notification may have
+ // previously been dismissed & entry.isRowDismissed would still be true
+ boolean userRemovedNotif = (entry.isRowDismissed() && !isAppCancel)
+ || isClearAll || isUserDimiss;
+
+ // The bubble notification sticks around in the data as long as the bubble is
+ // not dismissed and the app hasn't cancelled the notification.
+ boolean bubbleExtended = entry.isBubble() && !entry.isBubbleDismissed()
+ && userRemovedNotif;
+ if (bubbleExtended) {
+ entry.setShowInShadeWhenBubble(false);
+ if (mStackView != null) {
+ mStackView.updateDotVisibility(entry.key);
+ }
+ mNotificationEntryManager.updateNotifications();
+ return true;
+ } else if (!userRemovedNotif && !entry.isBubbleDismissed()) {
+ // This wasn't a user removal so we should remove the bubble as well
+ mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
+ return false;
+ }
+ return false;
+ }
+ };
+
+ @SuppressWarnings("FieldCanBeLocal")
private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
@@ -396,7 +441,6 @@
return;
}
if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) {
- // TODO: handle group summaries?
updateShowInShadeForSuppressNotification(entry);
}
}
@@ -426,23 +470,6 @@
updateBubble(entry);
}
}
-
- @Override
- public void onEntryRemoved(NotificationEntry entry,
- @Nullable NotificationVisibility visibility,
- boolean removedByUser) {
- if (!areBubblesEnabled(mContext)) {
- return;
- }
- entry.setShowInShadeWhenBubble(false);
- if (mStackView != null) {
- mStackView.updateDotVisibility(entry.key);
- }
- if (!removedByUser) {
- // This was a cancel so we should remove the bubble
- removeBubble(entry.key, DISMISS_NOTIF_CANCEL);
- }
- }
};
@SuppressWarnings("FieldCanBeLocal")
@@ -455,13 +482,15 @@
}
@Override
- public void onBubbleRemoved(Bubble bubble, int reason) {
+ public void onBubbleRemoved(Bubble bubble, @DismissReason int reason) {
if (mStackView != null) {
mStackView.removeBubble(bubble);
}
- if (!bubble.entry.showInShadeWhenBubble()) {
- // The notification is gone & bubble is gone, time to actually remove it
- mNotificationEntryManager.performRemoveNotification(bubble.entry.notification);
+ if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
+ && !bubble.entry.showInShadeWhenBubble()) {
+ // The bubble is gone & the notification is gone, time to actually remove it
+ mNotificationEntryManager.performRemoveNotification(bubble.entry.notification,
+ 0 /* reason */);
} else {
// The notification is still in the shade but we've removed the bubble so
// lets make sure NoMan knows it's not a bubble anymore
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index a63fdbd..d4e7bbc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -32,6 +32,7 @@
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityManager;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dependency;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.analytics.DataCollector;
@@ -62,6 +63,8 @@
Sensor.TYPE_LIGHT,
Sensor.TYPE_ROTATION_VECTOR,
};
+ private static final String FALSING_REMAIN_LOCKED = "falsing_failure_after_attempts";
+ private static final String FALSING_SUCCESS = "falsing_success_after_attempts";
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Context mContext;
@@ -83,6 +86,8 @@
private boolean mScreenOn;
private boolean mShowingAod;
private Runnable mPendingWtf;
+ private int mIsFalseTouchCalls;
+ private MetricsLogger mMetricsLogger;
protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
@@ -99,6 +104,7 @@
mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
mUiOffloadThread = Dependency.get(UiOffloadThread.class);
mScreenOn = context.getSystemService(PowerManager.class).isInteractive();
+ mMetricsLogger = new MetricsLogger();
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
@@ -143,6 +149,14 @@
private void sessionExitpoint(boolean force) {
if (mSessionActive && (force || !shouldSessionBeActive())) {
mSessionActive = false;
+ if (mIsFalseTouchCalls != 0) {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i(
+ "isFalseTouchCalls", "Calls before failure: " + mIsFalseTouchCalls);
+ }
+ mMetricsLogger.histogram(FALSING_REMAIN_LOCKED, mIsFalseTouchCalls);
+ mIsFalseTouchCalls = 0;
+ }
// This can be expensive, and doesn't need to happen on the main thread.
mUiOffloadThread.submit(() -> {
@@ -166,6 +180,7 @@
}
mBouncerOn = false;
mSessionActive = true;
+ mIsFalseTouchCalls = 0;
if (mHumanInteractionClassifier.isEnabled()) {
registerSensors(CLASSIFIER_SENSORS);
@@ -250,7 +265,16 @@
// anti-falsed.
return false;
}
- return mHumanInteractionClassifier.isFalseTouch();
+ mIsFalseTouchCalls++;
+ boolean isFalse = mHumanInteractionClassifier.isFalseTouch();
+ if (!isFalse) {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("isFalseTouchCalls", "Calls before success: " + mIsFalseTouchCalls);
+ }
+ mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls);
+ mIsFalseTouchCalls = 0;
+ }
+ return isFalse;
}
private void clearPendingWtf() {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 68d792e..d6c8971 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -413,6 +413,7 @@
},
mKeyguardManager.isDeviceLocked())
: null;
+
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.setKeyguardShowing(mKeyguardShowing);
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
index 477e7d7e..24a4b9e 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
@@ -30,13 +30,15 @@
import android.util.Log;
/**
- * A helper class that computes histogram and percentile 85 from a bitmap.
- * Percentile 85 will be computed each time the user picks a new image wallpaper.
+ * A helper class that computes threshold from a bitmap.
+ * Threshold will be computed each time the user picks a new image wallpaper.
*/
class ImageProcessHelper {
private static final String TAG = ImageProcessHelper.class.getSimpleName();
- private static final float DEFAULT_PER85 = 0.8f;
- private static final int MSG_UPDATE_PER85 = 1;
+ private static final float DEFAULT_THRESHOLD = 0.8f;
+ private static final float DEFAULT_OTSU_THRESHOLD = 0f;
+ private static final float MAX_THRESHOLD = 0.89f;
+ private static final int MSG_UPDATE_THRESHOLD = 1;
/**
* This color matrix will be applied to each pixel to get luminance from rgb by below formula:
@@ -53,8 +55,8 @@
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
- case MSG_UPDATE_PER85:
- mPer85 = (float) msg.obj;
+ case MSG_UPDATE_THRESHOLD:
+ mThreshold = (float) msg.obj;
return true;
default:
return false;
@@ -62,20 +64,20 @@
}
});
- private float mPer85 = DEFAULT_PER85;
+ private float mThreshold = DEFAULT_THRESHOLD;
- void startComputingPercentile85(Bitmap bitmap) {
- new Per85ComputeTask(mHandler).execute(bitmap);
+ void start(Bitmap bitmap) {
+ new ThresholdComputeTask(mHandler).execute(bitmap);
}
- float getPercentile85() {
- return mPer85;
+ float getThreshold() {
+ return Math.min(mThreshold, MAX_THRESHOLD);
}
- private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> {
+ private static class ThresholdComputeTask extends AsyncTask<Bitmap, Void, Float> {
private Handler mUpdateHandler;
- Per85ComputeTask(Handler handler) {
+ ThresholdComputeTask(Handler handler) {
super(handler);
mUpdateHandler = handler;
}
@@ -84,35 +86,55 @@
protected Float doInBackground(Bitmap... bitmaps) {
Bitmap bitmap = bitmaps[0];
if (bitmap != null) {
- int[] histogram = processHistogram(bitmap);
- return computePercentile85(bitmap, histogram);
+ return new Threshold().compute(bitmap);
}
- Log.e(TAG, "Per85ComputeTask: Can't get bitmap");
- return DEFAULT_PER85;
+ Log.e(TAG, "ThresholdComputeTask: Can't get bitmap");
+ return DEFAULT_THRESHOLD;
}
@Override
protected void onPostExecute(Float result) {
- Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result);
+ Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_THRESHOLD, result);
mUpdateHandler.sendMessage(msg);
}
+ }
- private int[] processHistogram(Bitmap bitmap) {
+ private static class Threshold {
+ public float compute(Bitmap bitmap) {
+ Bitmap grayscale = toGrayscale(bitmap);
+ int[] histogram = getHistogram(grayscale);
+ boolean isSolidColor = isSolidColor(grayscale, histogram);
+
+ // We will see gray wallpaper during the transition if solid color wallpaper is set,
+ // please refer to b/130360362#comment16.
+ // As a result, we use Percentile85 rather than Otsus if a solid color wallpaper is set.
+ ThresholdAlgorithm algorithm = isSolidColor ? new Percentile85() : new Otsus();
+ return algorithm.compute(grayscale, histogram);
+ }
+
+ private Bitmap toGrayscale(Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
- Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig());
- Canvas canvas = new Canvas(target);
+ Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig());
+ Canvas canvas = new Canvas(grayscale);
ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(cm));
canvas.drawBitmap(bitmap, new Matrix(), paint);
+ return grayscale;
+ }
+
+ private int[] getHistogram(Bitmap grayscale) {
+ int width = grayscale.getWidth();
+ int height = grayscale.getHeight();
+
// TODO: Fine tune the performance here, tracking on b/123615079.
int[] histogram = new int[256];
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
- int pixel = target.getPixel(col, row);
+ int pixel = grayscale.getPixel(col, row);
int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel);
histogram[y]++;
}
@@ -121,8 +143,29 @@
return histogram;
}
- private float computePercentile85(Bitmap bitmap, int[] histogram) {
- float per85 = DEFAULT_PER85;
+ private boolean isSolidColor(Bitmap bitmap, int[] histogram) {
+ boolean solidColor = false;
+ int pixels = bitmap.getWidth() * bitmap.getHeight();
+
+ // In solid color case, only one element of histogram has value,
+ // which is pixel counts and the value of other elements should be 0.
+ for (int value : histogram) {
+ if (value != 0 && value != pixels) {
+ break;
+ }
+ if (value == pixels) {
+ solidColor = true;
+ break;
+ }
+ }
+ return solidColor;
+ }
+ }
+
+ private static class Percentile85 implements ThresholdAlgorithm {
+ @Override
+ public float compute(Bitmap bitmap, int[] histogram) {
+ float per85 = DEFAULT_THRESHOLD;
int pixelCount = bitmap.getWidth() * bitmap.getHeight();
float[] acc = new float[256];
for (int i = 0; i < acc.length; i++) {
@@ -141,4 +184,51 @@
return per85;
}
}
+
+ private static class Otsus implements ThresholdAlgorithm {
+ @Override
+ public float compute(Bitmap bitmap, int[] histogram) {
+ float threshold = DEFAULT_OTSU_THRESHOLD;
+ float maxVariance = 0;
+ float pixelCount = bitmap.getWidth() * bitmap.getHeight();
+ float[] w = new float[2];
+ float[] m = new float[2];
+ float[] u = new float[2];
+
+ for (int i = 0; i < histogram.length; i++) {
+ m[1] += i * histogram[i];
+ }
+
+ w[1] = pixelCount;
+ for (int tonalValue = 0; tonalValue < histogram.length; tonalValue++) {
+ float dU;
+ float variance;
+ float numPixels = histogram[tonalValue];
+ float tmp = numPixels * tonalValue;
+ w[0] += numPixels;
+ w[1] -= numPixels;
+
+ if (w[0] == 0 || w[1] == 0) {
+ continue;
+ }
+
+ m[0] += tmp;
+ m[1] -= tmp;
+ u[0] = m[0] / w[0];
+ u[1] = m[1] / w[1];
+ dU = u[0] - u[1];
+ variance = w[0] * w[1] * dU * dU;
+
+ if (variance > maxVariance) {
+ threshold = (tonalValue + 1f) / histogram.length;
+ maxVariance = variance;
+ }
+ }
+ return threshold;
+ }
+ }
+
+ private interface ThresholdAlgorithm {
+ float compute(Bitmap bitmap, int[] histogram);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 464cbe3..5bbfe84 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -78,8 +78,8 @@
mBitmap = mWallpaperManager.getBitmap();
mBitmapWidth = mBitmap.getWidth();
mBitmapHeight = mBitmap.getHeight();
- // Compute per85 as transition threshold, this is an async work.
- mImageProcessHelper.startComputingPercentile85(mBitmap);
+ // Compute threshold of the image, this is an async work.
+ mImageProcessHelper.start(mBitmap);
mWallpaperManager.forgetLoadedWallpaper();
}
}
@@ -108,13 +108,13 @@
@Override
public void onDrawFrame(GL10 gl) {
- float per85 = mImageProcessHelper.getPercentile85();
+ float threshold = mImageProcessHelper.getThreshold();
float reveal = mImageRevealHelper.getReveal();
glClear(GL_COLOR_BUFFER_BIT);
glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1);
- glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), per85);
+ glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), threshold);
glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
scaleViewport(reveal);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
index e352b58..4571ef3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
@@ -77,6 +77,7 @@
@Override
public void onClick(View v) {
+ if (!v.isVisibleToUser()) return;
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
Settings.ACTION_WIRELESS_SETTINGS), 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 9431f20..42c616c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -524,7 +524,7 @@
if (v == mClockView) {
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS), 0);
- } else if (v == mNextAlarmContainer) {
+ } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) {
if (mNextAlarm.getShowIntent() != null) {
mActivityStarter.postStartActivityDismissingKeyguard(
mNextAlarm.getShowIntent());
@@ -545,7 +545,7 @@
new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
mHost.collapsePanels();
});
- } else if (v == mRingerContainer) {
+ } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
Settings.ACTION_SOUND_SETTINGS), 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index ca04076..9282a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -144,7 +144,8 @@
mContext.getString(R.string.accessibility_bluetooth_name, state.label)
+ ", " + state.secondaryLabel;
} else if (state.isTransient) {
- state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation);
+ state.icon = ResourceIcon.get(
+ com.android.internal.R.drawable.ic_bluetooth_transient_animation);
state.contentDescription = state.secondaryLabel;
} else {
state.icon =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 5e6f18e..001e094 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -130,7 +130,8 @@
state.isTransient = isTransient;
state.slash.isSlashed = !state.value && !state.isTransient;
if (state.isTransient) {
- state.icon = ResourceIcon.get(R.drawable.ic_hotspot_transient_animation);
+ state.icon = ResourceIcon.get(
+ com.android.internal.R.drawable.ic_hotspot_transient_animation);
}
state.expandedAccessibilityClassName = Switch.class.getName();
state.contentDescription = state.label;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 15df1f1..0e7362c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -186,7 +186,8 @@
final StringBuffer minimalContentDescription = new StringBuffer();
final Resources r = mContext.getResources();
if (isTransient) {
- state.icon = ResourceIcon.get(R.drawable.ic_signal_wifi_transient_animation);
+ state.icon = ResourceIcon.get(
+ com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
state.label = r.getString(R.string.quick_settings_wifi_label);
} else if (!state.value) {
state.slash.isSlashed = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
index b7ae4ed..4b2d131 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar
-import android.annotation.ColorInt
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
@@ -28,6 +27,7 @@
import android.renderscript.ScriptIntrinsicBlur
import android.util.MathUtils
import com.android.internal.graphics.ColorUtils
+import com.android.systemui.statusbar.notification.MediaNotificationProcessor
import javax.inject.Inject
import javax.inject.Singleton
@@ -42,7 +42,7 @@
private val mTmpSize = Point()
private var mArtworkCache: Bitmap? = null
- fun processArtwork(context: Context, artwork: Bitmap, @ColorInt color: Int): Bitmap {
+ fun processArtwork(context: Context, artwork: Bitmap): Bitmap {
if (mArtworkCache != null) {
return mArtworkCache!!
}
@@ -71,13 +71,15 @@
blur.forEach(output)
output.copyTo(outBitmap)
+ val swatch = MediaNotificationProcessor.findBackgroundSwatch(artwork)
+
input.destroy()
output.destroy()
inBitmap.recycle()
blur.destroy()
val canvas = Canvas(outBitmap)
- canvas.drawColor(ColorUtils.setAlphaComponent(color, COLOR_ALPHA))
+ canvas.drawColor(ColorUtils.setAlphaComponent(swatch.rgb, COLOR_ALPHA))
return outBitmap
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index aeaceb0..d59a5e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -104,7 +104,7 @@
// Remove existing notification to avoid stale data.
if (isUpdate) {
- mEntryManager.removeNotification(key, rankingMap);
+ mEntryManager.removeNotification(key, rankingMap, 0 /* reason */);
} else {
mEntryManager.getNotificationData()
.updateRanking(rankingMap);
@@ -121,18 +121,23 @@
}
@Override
- public void onNotificationRemoved(StatusBarNotification sbn,
- final RankingMap rankingMap) {
- if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+ int reason) {
+ if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn + " reason: " + reason);
if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
final String key = sbn.getKey();
Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
- mEntryManager.removeNotification(key, rankingMap);
+ mEntryManager.removeNotification(key, rankingMap, reason);
});
}
}
@Override
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
+ onNotificationRemoved(sbn, rankingMap, 0 /* reason */);
+ }
+
+ @Override
public void onNotificationRankingUpdate(final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onRankingUpdate");
if (rankingMap != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 1615e65..b9e0c60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -21,11 +21,11 @@
import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER;
import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
+import android.annotation.MainThread;
import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -35,11 +35,13 @@
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
+import android.os.AsyncTask;
import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
+import android.util.ArraySet;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
@@ -64,8 +66,10 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -108,6 +112,7 @@
private final MediaSessionManager mMediaSessionManager;
private final ArrayList<MediaListener> mMediaListeners;
private final MediaArtworkProcessor mMediaArtworkProcessor;
+ private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
protected NotificationPresenter mPresenter;
private MediaController mMediaController;
@@ -449,28 +454,37 @@
+ " state=" + mStatusBarStateController.getState());
}
- Drawable artworkDrawable = null;
+ Bitmap artworkBitmap = null;
if (mediaMetadata != null) {
- Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
+ artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
if (artworkBitmap == null) {
artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
- // might still be null
}
- if (artworkBitmap != null) {
- int notificationColor;
- synchronized (mEntryManager.getNotificationData()) {
- NotificationEntry entry = mEntryManager.getNotificationData()
- .get(mMediaNotificationKey);
- if (entry == null || entry.getRow() == null) {
- notificationColor = Color.TRANSPARENT;
- } else {
- notificationColor = entry.getRow().calculateBgColor();
- }
- }
- Bitmap bmp = mMediaArtworkProcessor.processArtwork(mContext, artworkBitmap,
- notificationColor);
- artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
+ }
+
+ // Process artwork on a background thread and send the resulting bitmap to
+ // finishUpdateMediaMetaData.
+ if (metaDataChanged) {
+ for (AsyncTask<?, ?, ?> task : mProcessArtworkTasks) {
+ task.cancel(true);
}
+ mProcessArtworkTasks.clear();
+ }
+ if (artworkBitmap != null) {
+ mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged,
+ allowEnterAnimation).execute(artworkBitmap));
+ } else {
+ finishUpdateMediaMetaData(metaDataChanged, allowEnterAnimation, null);
+ }
+
+ Trace.endSection();
+ }
+
+ private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation,
+ @Nullable Bitmap bmp) {
+ Drawable artworkDrawable = null;
+ if (bmp != null) {
+ artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
}
boolean allowWhenShade = false;
if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
@@ -598,7 +612,6 @@
}
}
}
- Trace.endSection();
}
public void setup(BackDropView backdrop, ImageView backdropFront, ImageView backdropBack,
@@ -629,6 +642,61 @@
}
};
+ private Bitmap processArtwork(Bitmap artwork) {
+ return mMediaArtworkProcessor.processArtwork(mContext, artwork);
+ }
+
+ @MainThread
+ private void removeTask(AsyncTask<?, ?, ?> task) {
+ mProcessArtworkTasks.remove(task);
+ }
+
+ /**
+ * {@link AsyncTask} to prepare album art for use as backdrop on lock screen.
+ */
+ private static final class ProcessArtworkTask extends AsyncTask<Bitmap, Void, Bitmap> {
+
+ private final WeakReference<NotificationMediaManager> mManagerRef;
+ private final boolean mMetaDataChanged;
+ private final boolean mAllowEnterAnimation;
+
+ ProcessArtworkTask(NotificationMediaManager manager, boolean changed,
+ boolean allowAnimation) {
+ mManagerRef = new WeakReference<>(manager);
+ mMetaDataChanged = changed;
+ mAllowEnterAnimation = allowAnimation;
+ }
+
+ @Override
+ protected Bitmap doInBackground(Bitmap... bitmaps) {
+ NotificationMediaManager manager = mManagerRef.get();
+ if (manager == null || bitmaps.length == 0 || isCancelled()) {
+ return null;
+ }
+ return manager.processArtwork(bitmaps[0]);
+ }
+
+ @Override
+ protected void onPostExecute(@Nullable Bitmap result) {
+ NotificationMediaManager manager = mManagerRef.get();
+ if (manager != null && !isCancelled()) {
+ manager.removeTask(this);
+ manager.finishUpdateMediaMetaData(mMetaDataChanged, mAllowEnterAnimation, result);
+ }
+ }
+
+ @Override
+ protected void onCancelled(Bitmap result) {
+ if (result != null) {
+ result.recycle();
+ }
+ NotificationMediaManager manager = mManagerRef.get();
+ if (manager != null) {
+ manager.removeTask(this);
+ }
+ }
+ }
+
public interface MediaListener {
void onMetadataChanged(MediaMetadata metadata);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 2793b2a..fe8c6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -51,6 +51,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
+import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -273,10 +274,20 @@
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
+ public void onPreEntryUpdated(NotificationEntry entry) {
+ // Mark smart replies as sent whenever a notification is updated - otherwise the
+ // smart replies are never marked as sent.
+ mSmartReplyController.stopSending(entry);
+ }
+
+ @Override
public void onEntryRemoved(
@Nullable NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser) {
+ // We're removing the notification, the smart controller can forget about it.
+ mSmartReplyController.stopSending(entry);
+
if (removedByUser && entry != null) {
onPerformRemoveNotification(entry, entry.key);
}
@@ -348,24 +359,18 @@
ViewParent p = view.getParent();
RemoteInputView riv = null;
+ ExpandableNotificationRow row = null;
while (p != null) {
if (p instanceof View) {
View pv = (View) p;
if (pv.isRootNamespace()) {
riv = findRemoteInputView(pv);
+ row = (ExpandableNotificationRow) pv.getTag(R.id.row_tag_for_content_view);
break;
}
}
p = p.getParent();
}
- ExpandableNotificationRow row = null;
- while (p != null) {
- if (p instanceof ExpandableNotificationRow) {
- row = (ExpandableNotificationRow) p;
- break;
- }
- p = p.getParent();
- }
if (row == null) {
return false;
@@ -391,10 +396,13 @@
if (riv == null) {
return false;
}
- if (!row.getPrivateLayout().getExpandedChild().isShown()) {
- mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
- return true;
- }
+ }
+ if (riv == row.getPrivateLayout().getExpandedRemoteInput()
+ && !row.getPrivateLayout().getExpandedChild().isShown()) {
+ // The expanded layout is selected, but it's not shown yet, let's wait on it to
+ // show before we do the animation.
+ mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
+ return true;
}
int width = view.getWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java
new file mode 100644
index 0000000..930116e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import android.service.notification.NotificationListenerService;
+
+/**
+ * Interface for anything that may need to prevent notifications from being removed. This is
+ * similar to a {@link NotificationLifetimeExtender} in the sense that it extends the life of
+ * a notification by preventing the removal, however, unlike the extender, the remove interceptor
+ * gets first pick at intercepting any type of removal -- the life time extender is unable to
+ * extend the life of a user dismissed or force removed notification.
+ */
+public interface NotificationRemoveInterceptor {
+
+ /**
+ * Called when a notification has been removed.
+ *
+ * @param key the entry key of the notification being removed.
+ * @param removeReason why the notification is being removed, e.g.
+ * {@link NotificationListenerService#REASON_CANCEL} or 0 if unknown.
+ *
+ * @return true if the removal should be ignored, false otherwise.
+ */
+ boolean onNotificationRemoveRequested(String key, int removeReason);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java
index 0044194..1ac8198 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java
@@ -37,8 +37,10 @@
*
* @param key Key identifying the notification to remove
* @param ranking RankingMap to update with
+ * @param reason why the notification is being removed, e.g.
+ * {@link NotificationListenerService#REASON_CANCEL}.
*/
- void removeNotification(String key, NotificationListenerService.RankingMap ranking);
+ void removeNotification(String key, NotificationListenerService.RankingMap ranking, int reason);
/**
* Update a given notification and the current notification ranking map.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index ab94008..5d1ab4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -69,8 +69,7 @@
private static final int RESIZE_BITMAP_AREA = 150 * 150;
private final ImageGradientColorizer mColorizer;
private final Context mContext;
- private float[] mFilteredBackgroundHsl = null;
- private Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl);
+ private final Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl);
/**
* The context of the notification. This is the app context of the package posting the
@@ -121,23 +120,21 @@
drawable.setBounds(0, 0, width, height);
drawable.draw(canvas);
- // for the background we only take the left side of the image to ensure
- // a smooth transition
- Palette.Builder paletteBuilder = Palette.from(bitmap)
- .setRegion(0, 0, bitmap.getWidth() / 2, bitmap.getHeight())
- .clearFilters() // we want all colors, red / white / black ones too!
- .resizeBitmapArea(RESIZE_BITMAP_AREA);
+ Palette.Builder paletteBuilder = generateArtworkPaletteBuilder(bitmap);
Palette palette = paletteBuilder.generate();
- backgroundColor = findBackgroundColorAndFilter(palette);
+ Palette.Swatch backgroundSwatch = findBackgroundSwatch(palette);
+ backgroundColor = backgroundSwatch.getRgb();
// we want most of the full region again, slightly shifted to the right
float textColorStartWidthFraction = 0.4f;
paletteBuilder.setRegion((int) (bitmap.getWidth() * textColorStartWidthFraction), 0,
bitmap.getWidth(),
bitmap.getHeight());
- if (mFilteredBackgroundHsl != null) {
+ // We're not filtering on white or black
+ if (!isWhiteOrBlack(backgroundSwatch.getHsl())) {
+ final float backgroundHue = backgroundSwatch.getHsl()[0];
paletteBuilder.addFilter((rgb, hsl) -> {
// at least 10 degrees hue difference
- float diff = Math.abs(hsl[0] - mFilteredBackgroundHsl[0]);
+ float diff = Math.abs(hsl[0] - backgroundHue);
return diff > 10 && diff < 350;
});
}
@@ -244,18 +241,31 @@
&& (swatch.getPopulation() / (float) RESIZE_BITMAP_AREA > MINIMUM_IMAGE_FRACTION);
}
- private int findBackgroundColorAndFilter(Palette palette) {
+ /**
+ * Finds an appropriate background swatch from media artwork.
+ *
+ * @param artwork Media artwork
+ * @return Swatch that should be used as the background of the media notification.
+ */
+ public static Palette.Swatch findBackgroundSwatch(Bitmap artwork) {
+ return findBackgroundSwatch(generateArtworkPaletteBuilder(artwork).generate());
+ }
+
+ /**
+ * Finds an appropriate background swatch from the palette of media artwork.
+ *
+ * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder}
+ * @return Swatch that should be used as the background of the media notification.
+ */
+ private static Palette.Swatch findBackgroundSwatch(Palette palette) {
// by default we use the dominant palette
Palette.Swatch dominantSwatch = palette.getDominantSwatch();
if (dominantSwatch == null) {
- // We're not filtering on white or black
- mFilteredBackgroundHsl = null;
- return Color.WHITE;
+ return new Palette.Swatch(Color.WHITE, 100);
}
if (!isWhiteOrBlack(dominantSwatch.getHsl())) {
- mFilteredBackgroundHsl = dominantSwatch.getHsl();
- return dominantSwatch.getRgb();
+ return dominantSwatch;
}
// Oh well, we selected black or white. Lets look at the second color!
List<Palette.Swatch> swatches = palette.getSwatches();
@@ -270,38 +280,51 @@
}
}
if (second == null) {
- // We're not filtering on white or black
- mFilteredBackgroundHsl = null;
- return dominantSwatch.getRgb();
+ return dominantSwatch;
}
if (dominantSwatch.getPopulation() / highestNonWhitePopulation
> POPULATION_FRACTION_FOR_WHITE_OR_BLACK) {
// The dominant swatch is very dominant, lets take it!
// We're not filtering on white or black
- mFilteredBackgroundHsl = null;
- return dominantSwatch.getRgb();
+ return dominantSwatch;
} else {
- mFilteredBackgroundHsl = second.getHsl();
- return second.getRgb();
+ return second;
}
}
- private boolean isWhiteOrBlack(float[] hsl) {
- return isBlack(hsl) || isWhite(hsl);
+ /**
+ * Generate a palette builder for media artwork.
+ *
+ * For producing a smooth background transition, the palette is extracted from only the left
+ * side of the artwork.
+ *
+ * @param artwork Media artwork
+ * @return Builder that generates the {@link Palette} for the media artwork.
+ */
+ private static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) {
+ // for the background we only take the left side of the image to ensure
+ // a smooth transition
+ return Palette.from(artwork)
+ .setRegion(0, 0, artwork.getWidth() / 2, artwork.getHeight())
+ .clearFilters() // we want all colors, red / white / black ones too!
+ .resizeBitmapArea(RESIZE_BITMAP_AREA);
}
+ private static boolean isWhiteOrBlack(float[] hsl) {
+ return isBlack(hsl) || isWhite(hsl);
+ }
/**
* @return true if the color represents a color which is close to black.
*/
- private boolean isBlack(float[] hslColor) {
+ private static boolean isBlack(float[] hslColor) {
return hslColor[2] <= BLACK_MAX_LIGHTNESS;
}
/**
* @return true if the color represents a color which is close to white.
*/
- private boolean isWhite(float[] hslColor) {
+ private static boolean isWhite(float[] hslColor) {
return hslColor[2] >= WHITE_MIN_LIGHTNESS;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 7d224fb..d926f88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -15,6 +15,9 @@
*/
package com.android.systemui.statusbar.notification;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_ERROR;
+
import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
@@ -30,6 +33,7 @@
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.NotificationUpdateHandler;
import com.android.systemui.statusbar.notification.collection.NotificationData;
@@ -82,6 +86,7 @@
final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
= new ArrayList<>();
private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
+ private NotificationRemoveInterceptor mRemoveInterceptor;
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -115,6 +120,11 @@
mNotificationEntryListeners.add(listener);
}
+ /** Sets the {@link NotificationRemoveInterceptor}. */
+ public void setNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
+ mRemoveInterceptor = interceptor;
+ }
+
/**
* Our dependencies can have cyclic references, so some need to be lazy
*/
@@ -146,7 +156,7 @@
/** Adds a {@link NotificationLifetimeExtender}. */
public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) {
mNotificationLifetimeExtenders.add(extender);
- extender.setCallback(key -> removeNotification(key, mLatestRankingMap));
+ extender.setCallback(key -> removeNotification(key, mLatestRankingMap, 0));
}
public NotificationData getNotificationData() {
@@ -158,10 +168,18 @@
updateNotifications();
}
- public void performRemoveNotification(StatusBarNotification n) {
+ /**
+ * Requests a notification to be removed.
+ *
+ * @param n the notification to remove.
+ * @param reason why it is being removed e.g. {@link NotificationListenerService#REASON_CANCEL},
+ * or 0 if unknown.
+ */
+ public void performRemoveNotification(StatusBarNotification n, int reason) {
final NotificationVisibility nv = obtainVisibility(n.getKey());
removeNotificationInternal(
- n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */);
+ n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */,
+ reason);
}
private NotificationVisibility obtainVisibility(String key) {
@@ -193,7 +211,8 @@
@Override
public void handleInflationException(StatusBarNotification n, Exception e) {
removeNotificationInternal(
- n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */);
+ n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */,
+ REASON_ERROR);
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onInflationError(n, e);
}
@@ -228,9 +247,10 @@
}
@Override
- public void removeNotification(String key, NotificationListenerService.RankingMap ranking) {
+ public void removeNotification(String key, NotificationListenerService.RankingMap ranking,
+ int reason) {
removeNotificationInternal(key, ranking, obtainVisibility(key), false /* forceRemove */,
- false /* removedByUser */);
+ false /* removedByUser */, reason);
}
private void removeNotificationInternal(
@@ -238,7 +258,15 @@
@Nullable NotificationListenerService.RankingMap ranking,
@Nullable NotificationVisibility visibility,
boolean forceRemove,
- boolean removedByUser) {
+ boolean removedByUser,
+ int reason) {
+
+ if (mRemoveInterceptor != null
+ && mRemoveInterceptor.onNotificationRemoveRequested(key, reason)) {
+ // Remove intercepted; skip
+ return;
+ }
+
final NotificationEntry entry = mNotificationData.get(key);
abortExistingInflation(key);
@@ -342,7 +370,8 @@
Dependency.get(LeakDetector.class).trackInstance(entry);
// Construct the expanded view.
- requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
+ requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
+ REASON_CANCEL));
abortExistingInflation(key);
@@ -383,7 +412,8 @@
listener.onPreEntryUpdated(entry);
}
- requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
+ requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
+ REASON_CANCEL));
updateNotifications();
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index d89354b..a3e18ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -247,7 +247,7 @@
*/
public boolean showInShadeWhenBubble() {
// We always show it in the shade if non-clearable
- return !isClearable() || mShowInShadeWhenBubble;
+ return !isRowDismissed() && (!isClearable() || mShowInShadeWhenBubble);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index c4ecb82..94f7e65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -270,6 +270,8 @@
}
}
+ // TODO: This method has side effects, it is NOT just logging that a notification
+ // was cleared, it also actually removes the notification
private void logNotificationClear(String key, StatusBarNotification notification,
NotificationVisibility nv) {
final String pkg = notification.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index b81d814..ad745f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -542,6 +542,12 @@
}
@Override
+ public void onViewAdded(View child) {
+ super.onViewAdded(child);
+ child.setTag(R.id.row_tag_for_content_view, mContainingNotification);
+ }
+
+ @Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
updateVisibility();
@@ -1893,4 +1899,8 @@
}
pw.println();
}
+
+ public RemoteInputView getExpandedRemoteInput() {
+ return mExpandedRemoteInput;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index b54de5a..f9a98ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -200,32 +200,40 @@
private boolean canSeekMedia() {
if (mMediaController == null || mMediaController.getPlaybackState() == null) {
+ Log.d(TAG, "Cannot seek media because the controller is invalid");
return false;
}
long actions = mMediaController.getPlaybackState().getActions();
+ Log.d(TAG, "Playback state actions are " + actions);
return (actions == 0 || (actions & PlaybackState.ACTION_SEEK_TO) != 0);
}
protected final Runnable mUpdatePlaybackUi = new Runnable() {
@Override
public void run() {
- if (mMediaController != null && mMediaController.getMetadata() != null
- && mSeekBar != null) {
- long position = mMediaController.getPlaybackState().getPosition();
- long duration = mMediaController.getMetadata().getLong(
- MediaMetadata.METADATA_KEY_DURATION);
+ if (mMediaController != null && mSeekBar != null) {
+ MediaMetadata metadata = mMediaController.getMetadata();
+ PlaybackState playbackState = mMediaController.getPlaybackState();
- if (mDuration != duration) {
- mDuration = duration;
- mSeekBar.setMax((int) mDuration);
- mSeekBarTotalTime.setText(millisecondsToTimeString(duration));
+ if (metadata != null && playbackState != null) {
+ long position = playbackState.getPosition();
+ long duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
+
+ if (mDuration != duration) {
+ mDuration = duration;
+ mSeekBar.setMax((int) mDuration);
+ mSeekBarTotalTime.setText(millisecondsToTimeString(duration));
+ }
+ mSeekBar.setProgress((int) position);
+
+ mSeekBarElapsedTime.setText(millisecondsToTimeString(position));
+ } else {
+ Log.d(TAG, "Controller missing data " + metadata + " " + playbackState);
+ clearTimer();
}
- mSeekBar.setProgress((int) position);
-
- mSeekBarElapsedTime.setText(millisecondsToTimeString(position));
} else {
- // We no longer have a media session / notification
+ Log.d(TAG, "No longer have a valid media controller");
clearTimer();
}
}
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 7a9da6b..0e7feaa 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
@@ -46,6 +46,7 @@
import android.os.Bundle;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -5586,7 +5587,8 @@
setDismissAllInProgress(false);
for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
if (canChildBeDismissed(rowToRemove)) {
- mEntryManager.removeNotification(rowToRemove.getEntry().key, null);
+ mEntryManager.removeNotification(rowToRemove.getEntry().key, null /* ranking */,
+ NotificationListenerService.REASON_CANCEL_ALL);
} else {
rowToRemove.resetTranslation();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index a831a5d..3b3bd21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import android.content.Context;
@@ -27,6 +28,7 @@
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricSourceType;
+import android.os.Handler;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -66,6 +68,7 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AccessibilityController mAccessibilityController;
private final DockManager mDockManager;
+ private final Handler mMainHandler;
private int mLastState = 0;
private boolean mTransientBiometricsError;
@@ -82,6 +85,8 @@
private boolean mLastBouncerVisible;
private int mIconColor;
private float mDozeAmount;
+ private int mIconRes;
+ private boolean mWasPulsingOnThisFrame;
private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */);
private final DockManager.DockEventListener mDockEventListener =
@@ -133,7 +138,8 @@
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
AccessibilityController accessibilityController,
- @Nullable DockManager dockManager) {
+ @Nullable DockManager dockManager,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
super(context, attrs);
mContext = context;
mUnlockMethodCache = UnlockMethodCache.getInstance(context);
@@ -142,6 +148,7 @@
mConfigurationController = configurationController;
mStatusBarStateController = statusBarStateController;
mDockManager = dockManager;
+ mMainHandler = mainHandler;
}
@Override
@@ -214,39 +221,36 @@
mPulsing, mLastDozing, mDozing, mBouncerVisible);
boolean isAnim = iconAnimRes != -1;
- Drawable icon;
- if (isAnim) {
- // Load the animation resource.
- icon = mContext.getDrawable(iconAnimRes);
- } else {
- // Load the static icon resource based on the current state.
- icon = getIconForState(state);
- }
+ int iconRes = isAnim ? iconAnimRes : getIconForState(state);
+ if (iconRes != mIconRes) {
+ mIconRes = iconRes;
- final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
- ? (AnimatedVectorDrawable) icon
- : null;
- setImageDrawable(icon, false);
- updateDarkTint();
- if (mIsFaceUnlockState) {
- announceForAccessibility(getContext().getString(
- R.string.accessibility_scanning_face));
- }
+ Drawable icon = mContext.getDrawable(iconRes);
+ final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
+ ? (AnimatedVectorDrawable) icon
+ : null;
+ setImageDrawable(icon, false);
+ if (mIsFaceUnlockState) {
+ announceForAccessibility(getContext().getString(
+ R.string.accessibility_scanning_face));
+ }
- if (animation != null && isAnim) {
- animation.forceAnimationOnUI();
- animation.clearAnimationCallbacks();
- animation.registerAnimationCallback(new Animatable2.AnimationCallback() {
- @Override
- public void onAnimationEnd(Drawable drawable) {
- if (getDrawable() == animation && state == getState()
- && doesAnimationLoop(iconAnimRes)) {
- animation.start();
+ if (animation != null && isAnim) {
+ animation.forceAnimationOnUI();
+ animation.clearAnimationCallbacks();
+ animation.registerAnimationCallback(new Animatable2.AnimationCallback() {
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ if (getDrawable() == animation && state == getState()
+ && doesAnimationLoop(iconAnimRes)) {
+ animation.start();
+ }
}
- }
- });
- animation.start();
+ });
+ animation.start();
+ }
}
+ updateDarkTint();
if (isAnim && !mLastScreenOn) {
removeCallbacks(mDrawOffTimeout);
@@ -300,7 +304,7 @@
}
}
- private Drawable getIconForState(int state) {
+ private int getIconForState(int state) {
int iconRes;
switch (state) {
case STATE_LOCKED:
@@ -318,25 +322,27 @@
throw new IllegalArgumentException();
}
- return mContext.getDrawable(iconRes);
+ return iconRes;
}
private boolean doesAnimationLoop(int resourceId) {
return resourceId == com.android.internal.R.anim.lock_scanning;
}
- private static int getAnimationResForTransition(int oldState, int newState,
+ private int getAnimationResForTransition(int oldState, int newState,
boolean wasPulsing, boolean pulsing, boolean wasDozing, boolean dozing,
boolean bouncerVisible) {
// Never animate when screen is off
- if (dozing && !pulsing) {
+ if (dozing && !pulsing && !mWasPulsingOnThisFrame) {
return -1;
}
boolean isError = oldState != STATE_BIOMETRICS_ERROR && newState == STATE_BIOMETRICS_ERROR;
boolean justUnlocked = oldState != STATE_LOCK_OPEN && newState == STATE_LOCK_OPEN;
boolean justLocked = oldState == STATE_LOCK_OPEN && newState == STATE_LOCKED;
+ boolean nowPulsing = !wasPulsing && pulsing;
+ boolean turningOn = wasDozing && !dozing && !mWasPulsingOnThisFrame;
if (isError) {
return com.android.internal.R.anim.lock_to_error;
@@ -346,7 +352,7 @@
return com.android.internal.R.anim.lock_lock;
} else if (newState == STATE_SCANNING_FACE && bouncerVisible) {
return com.android.internal.R.anim.lock_scanning;
- } else if (!wasPulsing && pulsing && newState != STATE_LOCK_OPEN) {
+ } else if ((nowPulsing || turningOn) && newState != STATE_LOCK_OPEN) {
return com.android.internal.R.anim.lock_in;
}
return -1;
@@ -377,6 +383,12 @@
*/
public void setPulsing(boolean pulsing) {
mPulsing = pulsing;
+ if (!mPulsing) {
+ mWasPulsingOnThisFrame = true;
+ mMainHandler.post(() -> {
+ mWasPulsingOnThisFrame = false;
+ });
+ }
update();
}
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 6fbb947..831d882 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -714,10 +714,10 @@
}
}
- public void onPanelExpandedChange(boolean expanded) {
+ public void onPanelExpandedChange() {
updateSlippery();
mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
- expanded, getContext().getDisplayId());
+ mPanelView.isFullyExpanded(), getContext().getDisplayId());
}
public void updateStates() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 1194a1d..d521809 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1316,7 +1316,11 @@
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == StatusBarState.KEYGUARD) {
animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- mQs.animateHeaderSlidingOut();
+ // Only animate header if the header is visible. If not, it will partially animate out
+ // the top of QS
+ if (!mQsExpanded) {
+ mQs.animateHeaderSlidingOut();
+ }
} else {
mKeyguardStatusBar.setAlpha(1f);
mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index ce1d638..68eba50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -277,7 +277,7 @@
super.panelExpansionChanged(frac, expanded);
updateScrimFraction();
if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
- mBar.getNavigationBarView().onPanelExpandedChange(expanded);
+ mBar.getNavigationBarView().onPanelExpandedChange();
}
}
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 1eceff7..1fc40b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -436,7 +436,6 @@
public void onUserSetupChanged() {
final boolean userSetup = mDeviceProvisionedController.isUserSetup(
mDeviceProvisionedController.getCurrentUser());
- // STOPSHIP(kozynski, b/129405675) Remove log
Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user "
+ mDeviceProvisionedController.getCurrentUser());
if (MULTIUSER_DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ce43253..93168db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -40,7 +40,6 @@
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dependency;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -192,9 +191,9 @@
KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
mStatusBarStateController.addCallback(this);
Dependency.get(ConfigurationController.class).addCallback(this);
- mLastGesturalNav = QuickStepContract.isGesturalMode(
+ mGesturalNav = QuickStepContract.isGesturalMode(
Dependency.get(NavigationModeController.class).addListener(this));
- mDockManager = SysUiServiceProvider.getComponent(context, DockManager.class);
+ mDockManager = Dependency.get(DockManager.class);
if (mDockManager != null) {
mDockManager.addListener(mDockEventListener);
mIsDocked = mDockManager.isDocked();
@@ -662,7 +661,13 @@
public boolean onBackPressed(boolean hideImmediately) {
if (mBouncer.isShowing()) {
mStatusBar.endAffordanceLaunch();
- reset(hideImmediately);
+ // The second condition is for SIM card locked bouncer
+ if (mBouncer.isScrimmed() && !mBouncer.needsFullscreenBouncer()) {
+ hideBouncer(false);
+ updateStates();
+ } else {
+ reset(hideImmediately);
+ }
return true;
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index e4af15c..e00d439 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static android.service.notification.NotificationListenerService.REASON_CLICK;
+
import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
import android.app.ActivityManager;
@@ -483,7 +485,7 @@
// We have to post it to the UI thread for synchronization
mMainThreadHandler.post(() -> {
Runnable removeRunnable =
- () -> mEntryManager.performRemoveNotification(notification);
+ () -> mEntryManager.performRemoveNotification(notification, REASON_CLICK);
if (mPresenter.isCollapsing()) {
// To avoid lags we're only performing the remove
// after the shade was collapsed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 471d511..0865eb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.view.View;
@@ -65,6 +66,7 @@
private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
private final Context mContext;
private final ActivityIntentHelper mActivityIntentHelper;
+ private final NotificationGroupManager mGroupManager;
private View mPendingWorkRemoteInputView;
private View mPendingRemoteInputView;
private final ShadeController mShadeController = Dependency.get(ShadeController.class);
@@ -72,11 +74,12 @@
private final CommandQueue mCommandQueue;
private int mDisabled2;
protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver();
+ private Handler mMainHandler = new Handler();
/**
*/
@Inject
- public StatusBarRemoteInputCallback(Context context) {
+ public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager) {
mContext = context;
mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
@@ -85,15 +88,15 @@
mCommandQueue = getComponent(context, CommandQueue.class);
mCommandQueue.addCallback(this);
mActivityIntentHelper = new ActivityIntentHelper(mContext);
+ mGroupManager = groupManager;
}
@Override
public void onStateChanged(int state) {
if (state == StatusBarState.SHADE && mStatusBarStateController.leaveOpenOnKeyguardHide()) {
if (!mStatusBarStateController.isKeyguardRequested()) {
- if (mPendingRemoteInputView != null
- && mPendingRemoteInputView.isAttachedToWindow()) {
- mPendingRemoteInputView.post(mPendingRemoteInputView::callOnClick);
+ if (mPendingRemoteInputView != null) {
+ mMainHandler.post(mPendingRemoteInputView::callOnClick);
}
mPendingRemoteInputView = null;
}
@@ -159,6 +162,10 @@
if (mKeyguardMonitor.isShowing()) {
onLockedRemoteInput(row, clickedView);
} else {
+ if (row.isChildInGroup() && !row.areChildrenExpanded()) {
+ // The group isn't expanded, let's make sure it's visible!
+ mGroupManager.toggleGroupExpansion(row.getStatusBarNotification());
+ }
row.setUserExpanded(true);
row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 9c7a1e3..3fc9b44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -212,13 +212,15 @@
@Nullable
private String generateTimeRemainingString() {
- if (mEstimate == null) {
- return null;
- }
+ synchronized (mFetchCallbacks) {
+ if (mEstimate == null) {
+ return null;
+ }
- String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0);
- return PowerUtil.getBatteryRemainingShortStringFormatted(
- mContext, mEstimate.getEstimateMillis());
+ String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0);
+ return PowerUtil.getBatteryRemainingShortStringFormatted(
+ mContext, mEstimate.getEstimateMillis());
+ }
}
private void updateEstimateInBackground() {
@@ -230,9 +232,11 @@
mFetchingEstimate = true;
Dependency.get(Dependency.BG_HANDLER).post(() -> {
// Only fetch the estimate if they are enabled
- mEstimate = null;
- if (mEstimates.isHybridNotificationEnabled()) {
- updateEstimate();
+ synchronized (mFetchCallbacks) {
+ mEstimate = null;
+ if (mEstimates.isHybridNotificationEnabled()) {
+ updateEstimate();
+ }
}
mFetchingEstimate = false;
Dependency.get(Dependency.MAIN_HANDLER).post(this::notifyEstimateFetchCallbacks);
@@ -240,9 +244,8 @@
}
private void notifyEstimateFetchCallbacks() {
- String estimate = generateTimeRemainingString();
-
synchronized (mFetchCallbacks) {
+ String estimate = generateTimeRemainingString();
for (EstimateFetchCompletion completion : mFetchCallbacks) {
completion.onBatteryRemainingEstimateRetrieved(estimate);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
index db2523e..98ab3e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
@@ -61,7 +61,6 @@
mSettingsObserver = new ContentObserver(mainHandler) {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
- // STOPSHIP(kozynski, b/129405675) Remove log
Log.d(TAG, "Setting change: " + uri);
if (mUserSetupUri.equals(uri)) {
notifySetupChanged();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 76f1684..3330d1e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -84,7 +84,6 @@
when(mMockInjectionInflationController.injectable(any())).thenReturn(inflater);
mFakeDockManager = new DockManagerFake();
- getContext().putComponent(DockManager.class, mFakeDockManager);
mCurrentUser = new MutableLiveData<>();
mCurrentUser.setValue(MAIN_USER_ID);
@@ -92,7 +91,7 @@
mClockManager = new ClockManager(getContext(), mMockInjectionInflationController,
mMockPluginManager, mMockColorExtractor, mMockContentResolver,
- mMockCurrentUserObserable, mMockSettingsWrapper);
+ mMockCurrentUserObserable, mMockSettingsWrapper, mFakeDockManager);
mClockManager.addOnClockChangedListener(mMockListener1);
mClockManager.addOnClockChangedListener(mMockListener2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 8b0ba94..ec8dae2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -18,13 +18,18 @@
import static android.app.Notification.FLAG_BUBBLE;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -50,6 +55,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -95,10 +101,13 @@
private FrameLayout mStatusBarView;
@Captor
private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
+ @Captor
+ private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
private TestableBubbleController mBubbleController;
private StatusBarWindowController mStatusBarWindowController;
private NotificationEntryListener mEntryListener;
+ private NotificationRemoveInterceptor mRemoveInterceptor;
private NotificationTestHelper mNotificationTestHelper;
private ExpandableNotificationRow mRow;
@@ -167,6 +176,10 @@
verify(mNotificationEntryManager, atLeastOnce())
.addNotificationEntryListener(mEntryListenerCaptor.capture());
mEntryListener = mEntryListenerCaptor.getValue();
+ // And the remove interceptor
+ verify(mNotificationEntryManager, atLeastOnce())
+ .setNotificationRemoveInterceptor(mRemoveInterceptorCaptor.capture());
+ mRemoveInterceptor = mRemoveInterceptorCaptor.getValue();
}
@Test
@@ -199,6 +212,27 @@
}
@Test
+ public void testRemoveBubble_withDismissedNotif() {
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertTrue(mRow.getEntry().showInShadeWhenBubble());
+
+ // Make it look like dismissed notif
+ mRow.getEntry().setShowInShadeWhenBubble(false);
+
+ // Now remove the bubble
+ mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
+
+ // Since the notif is dismissed, once the bubble is removed, performRemoveNotification gets
+ // called to really remove the notif
+ verify(mNotificationEntryManager, times(1)).performRemoveNotification(
+ mRow.getEntry().notification, 0);
+ assertFalse(mBubbleController.hasBubbles());
+ }
+
+ @Test
public void testDismissStack() {
mBubbleController.updateBubble(mRow.getEntry());
verify(mNotificationEntryManager, times(1)).updateNotifications();
@@ -455,8 +489,7 @@
mBubbleController.updateBubble(mRow.getEntry());
// Simulate notification cancellation.
- mEntryListener.onEntryRemoved(mRow.getEntry(), null /* notificationVisibility (unused) */,
- false /* removedbyUser */);
+ mRemoveInterceptor.onNotificationRemoveRequested(mRow.getEntry().key, REASON_APP_CANCEL);
mBubbleController.expandStackAndSelectBubble(key);
}
@@ -511,6 +544,83 @@
verify(mDeleteIntent, never()).send();
}
+ @Test
+ public void testRemoveBubble_succeeds_appCancel() {
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+
+ boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
+ mRow.getEntry().key, REASON_APP_CANCEL);
+
+ // Cancels always remove so no need to intercept
+ assertFalse(intercepted);
+ assertFalse(mBubbleController.hasBubbles());
+ }
+
+ @Test
+ public void removeBubble_fails_clearAll() {
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertTrue(mRow.getEntry().showInShadeWhenBubble());
+
+ boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
+ mRow.getEntry().key, REASON_CANCEL_ALL);
+
+ // Intercept!
+ assertTrue(intercepted);
+ // Should update show in shade state
+ assertFalse(mRow.getEntry().showInShadeWhenBubble());
+
+ verify(mNotificationEntryManager, never()).performRemoveNotification(
+ any(), anyInt());
+ assertTrue(mBubbleController.hasBubbles());
+ }
+
+ @Test
+ public void removeBubble_fails_userDismissNotif() {
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertTrue(mRow.getEntry().showInShadeWhenBubble());
+
+ boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
+ mRow.getEntry().key, REASON_CANCEL);
+
+ // Intercept!
+ assertTrue(intercepted);
+ // Should update show in shade state
+ assertFalse(mRow.getEntry().showInShadeWhenBubble());
+
+ verify(mNotificationEntryManager, never()).performRemoveNotification(
+ any(), anyInt());
+ assertTrue(mBubbleController.hasBubbles());
+ }
+
+ @Test
+ public void removeBubble_succeeds_userDismissBubble_userDimissNotif() {
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
+ mBubbleController.updateBubble(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertTrue(mRow.getEntry().showInShadeWhenBubble());
+
+ // Dismiss the bubble
+ mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
+ assertFalse(mBubbleController.hasBubbles());
+
+ // Dismiss the notification
+ boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
+ mRow.getEntry().key, REASON_CANCEL);
+
+ // It's no longer a bubble so we shouldn't intercept
+ assertFalse(intercepted);
+ }
+
static class TestableBubbleController extends BubbleController {
// Let's assume surfaces can be synchronized immediately.
TestableBubbleController(Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index df014a4..af2de1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -73,8 +73,6 @@
doReturn(false).when(mConfig).alwaysOnEnabled(anyInt());
mDockManagerFake = spy(new DockManagerFake());
- mContext.putComponent(DockManager.class, mDockManagerFake);
-
mDockHandler = new DozeDockHandler(mContext, mMachine, mHost, mConfig,
Handler.createAsync(Looper.myLooper()), mDockManagerFake);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 1ac6bef..6979fd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -38,7 +38,6 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.wakelock.WakeLock;
@@ -84,7 +83,6 @@
mSensors = new FakeSensorManager(mContext);
mWakeLock = new WakeLockFake();
mDockManagerFake = spy(new DockManagerFake());
- mContext.putComponent(DockManager.class, mDockManagerFake);
mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, mConfig, mParameters,
mSensors, Handler.createAsync(Looper.myLooper()), mWakeLock, true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 1a1acdf..0800cb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -99,7 +101,7 @@
public void testNotificationRemovalCallsRemoveNotification() {
mListener.onNotificationRemoved(mSbn, mRanking);
TestableLooper.get(this).processAllMessages();
- verify(mEntryManager).removeNotification(mSbn.getKey(), mRanking);
+ verify(mEntryManager).removeNotification(eq(mSbn.getKey()), eq(mRanking), anyInt());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
index b03abec..7eeae67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertNotSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -24,17 +26,22 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
+import android.annotation.Nullable;
import android.app.Notification;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.test.suitebuilder.annotation.SmallTest;
import android.widget.RemoteViews;
+import androidx.palette.graphics.Palette;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,9 +50,18 @@
@RunWith(AndroidJUnit4.class)
public class MediaNotificationProcessorTest extends SysuiTestCase {
+ private static final int BITMAP_WIDTH = 10;
+ private static final int BITMAP_HEIGHT = 10;
+
+ /**
+ * Color tolerance is borrowed from the AndroidX test utilities for Palette.
+ */
+ private static final int COLOR_TOLERANCE = 8;
+
private MediaNotificationProcessor mProcessor;
private Bitmap mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
private ImageGradientColorizer mColorizer;
+ @Nullable private Bitmap mArtwork;
@Before
public void setUp() {
@@ -53,6 +69,14 @@
mProcessor = new MediaNotificationProcessor(getContext(), getContext(), mColorizer);
}
+ @After
+ public void tearDown() {
+ if (mArtwork != null) {
+ mArtwork.recycle();
+ mArtwork = null;
+ }
+ }
+
@Test
public void testColorizedWithLargeIcon() {
Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon(
@@ -100,6 +124,36 @@
assertNotSame(contentView, remoteViews);
}
+ @Test
+ public void findBackgroundSwatch_white() {
+ // Given artwork that is completely white.
+ mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(mArtwork);
+ canvas.drawColor(Color.WHITE);
+ // WHEN the background swatch is computed
+ Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork);
+ // THEN the swatch color is white
+ assertCloseColors(swatch.getRgb(), Color.WHITE);
+ }
+
+ @Test
+ public void findBackgroundSwatch_red() {
+ // Given artwork that is completely red.
+ mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(mArtwork);
+ canvas.drawColor(Color.RED);
+ // WHEN the background swatch is computed
+ Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork);
+ // THEN the swatch color is red
+ assertCloseColors(swatch.getRgb(), Color.RED);
+ }
+
+ static void assertCloseColors(int expected, int actual) {
+ assertThat((float) Color.red(expected)).isWithin(COLOR_TOLERANCE).of(Color.red(actual));
+ assertThat((float) Color.green(expected)).isWithin(COLOR_TOLERANCE).of(Color.green(actual));
+ assertThat((float) Color.blue(expected)).isWithin(COLOR_TOLERANCE).of(Color.blue(actual));
+ }
+
public static class TestableColorizer extends ImageGradientColorizer {
private final Bitmap mBitmap;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index c8005dd..70941d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -16,13 +16,18 @@
package com.android.systemui.statusbar.notification;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@@ -51,6 +56,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.InitController;
@@ -61,6 +67,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -105,6 +112,7 @@
@Mock private ExpandableNotificationRow mRow;
@Mock private NotificationListContainer mListContainer;
@Mock private NotificationEntryListener mEntryListener;
+ @Mock private NotificationRemoveInterceptor mRemoveInterceptor;
@Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private NotificationListenerService.RankingMap mRankingMap;
@@ -234,6 +242,7 @@
Dependency.get(InitController.class).executePostInitTasks();
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
mEntryManager.addNotificationEntryListener(mEntryListener);
+ mEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
NotificationRowBinderImpl notificationRowBinder =
new NotificationRowBinderImpl(mContext, true /* allowLongPress */);
@@ -341,7 +350,7 @@
mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
- mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
+ mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, 0 /* reason */);
verify(mEntryListener, never()).onInflationError(any(), any());
@@ -357,7 +366,7 @@
public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
com.android.systemui.util.Assert.isNotMainThread();
- mEntryManager.removeNotification("not_a_real_key", mRankingMap);
+ mEntryManager.removeNotification("not_a_real_key", mRankingMap, 0 /* reason */);
verify(mEntryListener, never()).onEntryRemoved(
eq(mEntry), any(), eq(false) /* removedByUser */);
@@ -370,7 +379,7 @@
mEntryManager.setRowBinder(mMockedRowBinder);
mEntryManager.addNotification(mSbn, mRankingMap);
- mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
+ mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, 0 /* reason */);
verify(mEntryListener, never()).onEntryRemoved(
eq(mEntry), any(), eq(false /* removedByUser */));
@@ -449,7 +458,7 @@
mEntryManager.addNotificationLifetimeExtender(extender);
// WHEN the notification is removed
- mEntryManager.removeNotification(mEntry.key, mRankingMap);
+ mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
// THEN the extender is asked to manage the lifetime
verify(extender).setShouldManageLifetime(mEntry, true);
@@ -465,7 +474,7 @@
mEntryManager.getNotificationData().add(mEntry);
final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
mEntryManager.addNotificationLifetimeExtender(extender);
- mEntryManager.removeNotification(mEntry.key, mRankingMap);
+ mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
assertTrue(extender.isManaging(mEntry.key));
// WHEN the extender finishes its extension
@@ -485,7 +494,7 @@
NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
mEntryManager.addNotificationLifetimeExtender(extender);
- mEntryManager.removeNotification(mEntry.key, mRankingMap);
+ mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
// WHEN the notification is updated
mEntryManager.updateNotification(mEntry.notification, mRankingMap);
@@ -510,13 +519,13 @@
mEntryManager.addNotificationLifetimeExtender(extender2);
// GIVEN a notification was lifetime-extended and extender2 is managing it
- mEntryManager.removeNotification(mEntry.key, mRankingMap);
+ mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
verify(extender1, never()).setShouldManageLifetime(mEntry, true);
verify(extender2).setShouldManageLifetime(mEntry, true);
// WHEN the extender1 changes its mind and wants to extend the lifetime of the notif
when(extender1.shouldExtendLifetime(mEntry)).thenReturn(true);
- mEntryManager.removeNotification(mEntry.key, mRankingMap);
+ mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
// THEN extender2 stops managing the notif and extender1 starts managing it
verify(extender1).setShouldManageLifetime(mEntry, true);
@@ -530,7 +539,45 @@
@Test
public void testPerformRemoveNotification_removedEntry() {
mEntryManager.getNotificationData().remove(mSbn.getKey(), null /* ranking */);
- mEntryManager.performRemoveNotification(mSbn);
+ mEntryManager.performRemoveNotification(mSbn, REASON_CANCEL);
+ }
+
+ @Test
+ public void testRemoveInterceptor_interceptsDontGetRemoved() {
+ // GIVEN an entry manager with a notification
+ mEntryManager.setRowBinder(mMockedRowBinder);
+ mEntryManager.getNotificationData().add(mEntry);
+
+ // GIVEN interceptor that intercepts that entry
+ when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.key), anyInt()))
+ .thenReturn(true);
+
+ // WHEN the notification is removed
+ mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
+
+ // THEN the interceptor intercepts & the entry is not removed & no listeners are called
+ assertNotNull(mEntryManager.getNotificationData().get(mEntry.key));
+ verify(mEntryListener, never()).onEntryRemoved(eq(mEntry),
+ any(NotificationVisibility.class), anyBoolean());
+ }
+
+ @Test
+ public void testRemoveInterceptor_notInterceptedGetsRemoved() {
+ // GIVEN an entry manager with a notification
+ mEntryManager.setRowBinder(mMockedRowBinder);
+ mEntryManager.getNotificationData().add(mEntry);
+
+ // GIVEN interceptor that doesn't intercept
+ when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.key), anyInt()))
+ .thenReturn(false);
+
+ // WHEN the notification is removed
+ mEntryManager.removeNotification(mEntry.key, mRankingMap, 0 /* reason */);
+
+ // THEN the interceptor intercepts & the entry is not removed & no listeners are called
+ assertNull(mEntryManager.getNotificationData().get(mEntry.key));
+ verify(mEntryListener, atLeastOnce()).onEntryRemoved(eq(mEntry),
+ any(NotificationVisibility.class), anyBoolean());
}
private Notification.Action createAction() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 51fb47b..06d76eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static android.service.notification.NotificationListenerService.REASON_CLICK;
+
import static org.mockito.AdditionalAnswers.answerVoid;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -224,7 +226,7 @@
eq(sbn.getKey()), any(NotificationVisibility.class));
// Notification is removed due to FLAG_AUTO_CANCEL
- verify(mEntryManager).performRemoveNotification(eq(sbn));
+ verify(mEntryManager).performRemoveNotification(eq(sbn), eq(REASON_CLICK));
}
@Test
@@ -253,7 +255,7 @@
verifyZeroInteractions(mContentIntent);
// Notification should not be cancelled.
- verify(mEntryManager, never()).performRemoveNotification(eq(sbn));
+ verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
}
@Test
@@ -283,7 +285,7 @@
verifyZeroInteractions(mContentIntent);
// Notification should not be cancelled.
- verify(mEntryManager, never()).performRemoveNotification(eq(sbn));
+ verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
}
@Test
@@ -315,6 +317,6 @@
verifyNoMoreInteractions(mContentIntent);
// Notification should not be cancelled.
- verify(mEntryManager, never()).performRemoveNotification(eq(sbn));
+ verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index a88a595..a97832f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -70,7 +70,8 @@
mNotificationLockscreenUserManager);
mDependency.putComponent(CommandQueue.class, mock(CommandQueue.class));
- mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext));
+ mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
+ mock(NotificationGroupManager.class)));
mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver();
}
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml
new file mode 100644
index 0000000..1be546840e
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="150"
+ android:startOffset="0" android:valueFrom="0.3"
+ android:valueTo="0.3" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="150" android:valueFrom="0.3"
+ android:valueTo="1" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="483"
+ android:startOffset="167" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="650" android:valueFrom="1"
+ android:valueTo="0.3" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+</set>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml
new file mode 100644
index 0000000..c9fd424
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="317"
+ android:startOffset="0" android:valueFrom="0.3"
+ android:valueTo="0.3" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="317" android:valueFrom="0.3"
+ android:valueTo="1" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="483"
+ android:startOffset="333" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="817" android:valueFrom="1"
+ android:valueTo="0.3" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+</set>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml
new file mode 100644
index 0000000..b34d308
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="483"
+ android:startOffset="0" android:valueFrom="0.3"
+ android:valueTo="0.3" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="483" android:valueFrom="0.3"
+ android:valueTo="1" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="483"
+ android:startOffset="500" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="983" android:valueFrom="1"
+ android:valueTo="0.3" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+</set>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml
new file mode 100644
index 0000000..9d2b3a4f
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="fillAlpha" android:duration="650"
+ android:startOffset="0" android:valueFrom="0.3"
+ android:valueTo="0.3" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="650" android:valueFrom="0.3"
+ android:valueTo="1" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="483"
+ android:startOffset="667" android:valueFrom="1" android:valueTo="1"
+ android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+ <objectAnimator android:propertyName="fillAlpha" android:duration="17"
+ android:startOffset="1150" android:valueFrom="1"
+ android:valueTo="0.3" android:valueType="floatType"
+ android:interpolator="@*android:interpolator/transient_interpolator" />
+</set>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml
new file mode 100644
index 0000000..943893d
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="1250"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
new file mode 100644
index 0000000..8ca4520
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable">
+ <target android:name="_R_G_L_1_G_D_0_P_0"
+ android:animation="@*android:anim/ic_bluetooth_transient_animation_0"/>
+ <target android:name="_R_G_L_0_G_D_0_P_0"
+ android:animation="@*android:anim/ic_bluetooth_transient_animation_1"/>
+ <target android:name="time_group"
+ android:animation="@*android:anim/ic_bluetooth_transient_animation_2"/>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
new file mode 100644
index 0000000..66ac8fe
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+ android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G" android:translateX="3.75"
+ android:translateY="1.75">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M1.22 3.22 C0.93,3.51 0.93,3.99 1.22,4.28 C1.22,4.28 7.19,10.25 7.19,10.25 C7.19,10.25 1.22,16.22 1.22,16.22 C0.93,16.51 0.93,16.99 1.22,17.28 C1.37,17.43 1.56,17.5 1.75,17.5 C1.94,17.5 2.13,17.43 2.28,17.28 C2.28,17.28 7.25,12.31 7.25,12.31 C7.25,12.31 7.25,20.25 7.25,20.25 C7.25,20.25 8,20.25 8,20.25 C10.96,20.25 13.37,17.84 13.37,14.88 C13.37,12.91 12.31,11.19 10.73,10.25 C12.31,9.31 13.37,7.59 13.37,5.63 C13.37,2.66 10.96,0.25 8,0.25 C8,0.25 7.25,0.25 7.25,0.25 C7.25,0.25 7.25,8.19 7.25,8.19 C7.25,8.19 2.28,3.22 2.28,3.22 C1.99,2.93 1.51,2.93 1.22,3.22c M8.75 1.82 C10.52,2.17 11.87,3.75 11.87,5.63 C11.87,7.5 10.52,9.08 8.75,9.43 C8.75,9.43 8.75,1.82 8.75,1.82c M8.75 11.07 C10.52,11.42 11.87,13 11.87,14.88 C11.87,16.75 10.52,18.33 8.75,18.68 C8.75,18.68 8.75,11.07 8.75,11.07c "/>
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="3.75"
+ android:translateY="1.75">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M15.25 9.25 C14.7,9.25 14.25,9.7 14.25,10.25 C14.25,10.8 14.7,11.25 15.25,11.25 C15.8,11.25 16.25,10.8 16.25,10.25 C16.25,9.7 15.8,9.25 15.25,9.25c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="3.75"
+ android:translateY="1.75">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M0.25 10.25 C0.25,10.8 0.7,11.25 1.25,11.25 C1.8,11.25 2.25,10.8 2.25,10.25 C2.25,9.7 1.8,9.25 1.25,9.25 C0.7,9.25 0.25,9.7 0.25,10.25c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
new file mode 100644
index 0000000..1317f66
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable">
+ <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/>
+ <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/>
+ <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/>
+ <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
new file mode 100644
index 0000000..8153195
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G" android:translateX="2"
+ android:translateY="1.552999999999999">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M10 12.45 C9.17,12.45 8.5,11.77 8.5,10.95 C8.5,10.12 9.17,9.45 10,9.45 C10.83,9.45 11.5,10.12 11.5,10.95 C11.5,11.77 10.83,12.45 10,12.45c "/>
+ <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M6.47 15.23 C6.27,15.23 6.08,15.16 5.94,15.01 C4.85,13.93 4.25,12.48 4.25,10.95 C4.25,9.41 4.85,7.97 5.94,6.88 C8.18,4.64 11.82,4.64 14.07,6.88 C15.15,7.97 15.75,9.41 15.75,10.95 C15.75,12.48 15.15,13.93 14.07,15.01 C13.77,15.3 13.3,15.3 13.01,15.01 C12.71,14.72 12.71,14.24 13.01,13.95 C13.81,13.15 14.25,12.08 14.25,10.95 C14.25,9.81 13.81,8.74 13.01,7.94 C11.35,6.28 8.65,6.28 6.99,7.94 C6.19,8.75 5.75,9.81 5.75,10.95 C5.75,12.08 6.19,13.15 6.99,13.95 C7.29,14.25 7.29,14.72 6.99,15.01 C6.85,15.16 6.66,15.23 6.47,15.23c "/>
+ <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M16.36 18.06 C16.17,18.06 15.98,17.99 15.83,17.84 C15.54,17.55 15.54,17.07 15.83,16.78 C17.39,15.22 18.25,13.15 18.25,10.95 C18.25,8.74 17.39,6.67 15.83,5.11 C12.62,1.9 7.38,1.9 4.17,5.11 C2.61,6.67 1.75,8.74 1.75,10.95 C1.75,13.15 2.61,15.22 4.17,16.78 C4.46,17.07 4.46,17.55 4.17,17.84 C3.87,18.13 3.4,18.13 3.11,17.84 C1.26,16 0.25,13.55 0.25,10.95 C0.25,8.34 1.26,5.89 3.11,4.05 C6.91,0.25 13.09,0.25 16.89,4.05 C18.74,5.89 19.75,8.34 19.75,10.95 C19.75,13.55 18.74,16 16.89,17.84 C16.75,17.99 16.56,18.06 16.36,18.06c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
new file mode 100644
index 0000000..06755f6
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable">
+ <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/>
+ <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/>
+ <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/>
+ <target android:name="_R_G_L_0_G_D_3_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/>
+ <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
new file mode 100644
index 0000000..6e45513
--- /dev/null
+++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G" android:translateX="0.6440000000000001"
+ android:translateY="2.755000000000001">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="0.3" android:fillType="nonZero"
+ android:pathData=" M12.86 15.75 C12.86,16.58 12.19,17.25 11.36,17.25 C10.53,17.25 9.86,16.58 9.86,15.75 C9.86,14.92 10.53,14.25 11.36,14.25 C12.19,14.25 12.86,14.92 12.86,15.75c "/>
+ <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+ android:fillAlpha="0.3" android:fillType="nonZero"
+ android:pathData=" M15.61 12.25 C15.42,12.25 15.23,12.17 15.08,12.03 C14.09,11.04 12.76,10.5 11.36,10.5 C9.95,10.5 8.63,11.04 7.63,12.03 C7.34,12.32 6.86,12.32 6.57,12.02 C6.28,11.73 6.28,11.26 6.58,10.96 C7.86,9.7 9.55,9 11.36,9 C13.16,9 14.86,9.7 16.14,10.96 C16.43,11.25 16.43,11.73 16.14,12.02 C16,12.17 15.8,12.25 15.61,12.25c "/>
+ <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+ android:fillAlpha="0.3" android:fillType="nonZero"
+ android:pathData=" M18.77 9.08 C18.58,9.08 18.39,9.01 18.24,8.86 C16.4,7.02 13.96,6 11.36,6 C8.75,6 6.31,7.02 4.47,8.86 C4.18,9.16 3.7,9.16 3.41,8.86 C3.12,8.57 3.11,8.1 3.41,7.8 C5.53,5.67 8.35,4.5 11.36,4.5 C14.36,4.5 17.18,5.67 19.31,7.8 C19.6,8.1 19.6,8.57 19.3,8.86 C19.16,9.01 18.97,9.08 18.77,9.08c "/>
+ <path android:name="_R_G_L_0_G_D_3_P_0" android:fillColor="#000000"
+ android:fillAlpha="0.3" android:fillType="nonZero"
+ android:pathData=" M21.96 5.89 C21.77,5.89 21.58,5.82 21.43,5.67 C18.74,2.98 15.16,1.5 11.36,1.5 C7.55,1.5 3.97,2.98 1.28,5.67 C0.99,5.97 0.51,5.97 0.22,5.67 C-0.07,5.38 -0.07,4.91 0.22,4.61 C3.19,1.64 7.15,0 11.36,0 C15.56,0 19.52,1.64 22.49,4.61 C22.78,4.91 22.78,5.38 22.49,5.67 C22.35,5.82 22.15,5.89 21.96,5.89c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
new file mode 100644
index 0000000..e884bd3
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable">
+ <target android:name="_R_G_L_1_G_D_0_P_0" android:animation="@*android:anim/ic_bluetooth_transient_animation_0" />
+ <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_bluetooth_transient_animation_1" />
+ <target android:name="time_group" android:animation="@*android:anim/ic_bluetooth_transient_animation_2" />
+</animated-vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
new file mode 100644
index 0000000..79107d8f
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+ android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G" android:translateX="3.099"
+ android:translateY="1.6400000000000006">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M10.41 10.36 C10.41,10.36 14.16,6.62 14.16,6.62 C14.57,6.21 14.57,5.55 14.16,5.14 C14.16,5.14 9.69,0.67 9.69,0.67 C9.69,0.67 9.66,0.64 9.66,0.64 C9.24,0.25 8.58,0.27 8.18,0.69 C8,0.88 7.9,1.14 7.9,1.4 C7.9,1.4 7.9,7.84 7.9,7.84 C7.9,7.84 3.85,3.79 3.85,3.79 C3.44,3.38 2.79,3.38 2.38,3.79 C1.97,4.2 1.97,4.85 2.38,5.26 C2.38,5.26 7.47,10.36 7.47,10.36 C7.47,10.36 2.38,15.45 2.38,15.45 C1.97,15.86 1.97,16.51 2.38,16.92 C2.79,17.33 3.44,17.33 3.85,16.92 C3.85,16.92 7.9,12.87 7.9,12.87 C7.9,12.87 7.9,19.32 7.9,19.32 C7.9,19.89 8.37,20.36 8.94,20.36 C9.2,20.36 9.46,20.26 9.65,20.08 C9.65,20.08 9.7,20.03 9.7,20.03 C9.7,20.03 14.16,15.57 14.16,15.57 C14.57,15.16 14.57,14.5 14.16,14.09 C14.16,14.09 10.41,10.36 10.41,10.36c M9.89 3.73 C9.89,3.73 12.04,5.88 12.04,5.88 C12.04,5.88 9.89,8.03 9.89,8.03 C9.89,8.03 9.89,3.73 9.89,3.73c M9.89 16.98 C9.89,16.98 9.89,12.68 9.89,12.68 C9.89,12.68 12.04,14.83 12.04,14.83 C12.04,14.83 9.89,16.98 9.89,16.98c "/>
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="3.099"
+ android:translateY="1.6400000000000006">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M16.96 9.3 C16.95,9.3 16.95,9.29 16.95,9.28 C16.36,8.71 15.42,8.72 14.84,9.3 C14.84,9.3 14.83,9.31 14.83,9.31 C14.25,9.9 14.25,10.84 14.84,11.42 C15.42,12.01 16.37,12.01 16.96,11.42 C17.55,10.84 17.55,9.89 16.96,9.3c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="3.099"
+ android:translateY="1.6400000000000006">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M2.96 9.3 C2.96,9.3 2.95,9.29 2.95,9.29 C2.36,8.71 1.42,8.71 0.84,9.3 C0.84,9.3 0.83,9.31 0.83,9.31 C0.25,9.9 0.25,10.84 0.84,11.42 C1.42,12.01 2.37,12.01 2.96,11.42 C3.55,10.83 3.55,9.89 2.96,9.3c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
new file mode 100644
index 0000000..54738c0
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable">
+ <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/>
+ <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/>
+ <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/>
+ <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
new file mode 100644
index 0000000..737b522
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+ android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G" android:translateX="1.545"
+ android:translateY="2.1449999999999996">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M9.31 0.92 C4.67,1.43 0.92,5.26 0.5,9.92 C0.25,12.64 1.1,15.17 2.65,17.1 C3.02,17.56 3.71,17.6 4.13,17.18 C4.49,16.82 4.52,16.25 4.21,15.86 C2.81,14.11 2.12,11.76 2.61,9.25 C3.22,6.12 5.75,3.6 8.89,3 C14,2.04 18.45,5.92 18.45,10.86 C18.45,12.75 17.79,14.48 16.69,15.86 C16.37,16.25 16.41,16.81 16.77,17.17 C16.77,17.17 16.77,17.17 16.77,17.17 C17.19,17.59 17.89,17.56 18.26,17.09 C19.64,15.39 20.45,13.22 20.45,10.86 C20.45,4.96 15.34,0.25 9.31,0.92c "/>
+ <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M13.91 14.31 C13.91,14.31 13.92,14.32 13.92,14.32 C14.35,14.75 15.08,14.71 15.42,14.21 C16.07,13.25 16.45,12.1 16.45,10.85 C16.45,7.11 13.01,4.16 9.12,5 C6.88,5.49 5.08,7.3 4.6,9.54 C4.22,11.28 4.61,12.92 5.48,14.2 C5.83,14.71 6.56,14.75 6.99,14.32 C6.99,14.32 7,14.31 7,14.31 C7.34,13.97 7.37,13.43 7.1,13.03 C6.6,12.26 6.36,11.32 6.49,10.29 C6.73,8.55 8.16,7.13 9.9,6.89 C12.36,6.56 14.46,8.46 14.46,10.85 C14.46,11.66 14.22,12.4 13.81,13.02 C13.54,13.43 13.57,13.97 13.91,14.31c "/>
+ <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.46 10.86 C12.46,11.96 11.56,12.86 10.46,12.86 C9.35,12.86 8.46,11.96 8.46,10.86 C8.46,9.75 9.35,8.86 10.46,8.86 C11.56,8.86 12.46,9.75 12.46,10.86c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
new file mode 100644
index 0000000..49d235b
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable">
+ <target android:name="_R_G_L_7_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/>
+ <target android:name="_R_G_L_6_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/>
+ <target android:name="_R_G_L_6_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/>
+ <target android:name="_R_G_L_5_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/>
+ <target android:name="_R_G_L_5_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/>
+ <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/>
+ <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/>
+ <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/>
+ <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/>
+ <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/>
+ <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/>
+ <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/>
+ <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/>
+ <target android:name="_R_G_L_0_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_7"/>
+ <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_8"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
new file mode 100644
index 0000000..9fcb479
--- /dev/null
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_7_G" android:translateX="0.10000000000000142"
+ android:translateY="3">
+ <path android:name="_R_G_L_7_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="0.3" android:fillType="nonZero"
+ android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/>
+ </group>
+ <group android:name="_R_G_L_6_G" android:translateX="0.10000000000000142"
+ android:translateY="3">
+ <path android:name="_R_G_L_6_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="0.3" android:fillType="nonZero"
+ android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/>
+ </group>
+ <group android:name="_R_G_L_5_G" android:translateX="5.81"
+ android:translateY="12.747">
+ <path android:name="_R_G_L_5_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M7.01 8.85 C7.01,8.85 12.12,2.5 12.12,2.5 C10.48,1.05 8.37,0.25 6.19,0.25 C3.91,0.25 1.84,1.1 0.25,2.5 C0.25,2.5 5.35,8.85 5.35,8.85 C5.78,9.38 6.58,9.38 7.01,8.85c "/>
+ </group>
+ <group android:name="_R_G_L_4_G" android:translateX="0.10000000000000142"
+ android:translateY="3">
+ <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="0.3" android:fillType="nonZero"
+ android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/>
+ </group>
+ <group android:name="_R_G_L_3_G" android:translateX="3.9290000000000003"
+ android:translateY="9.75">
+ <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M8.89 11.85 C8.89,11.85 15.88,3.15 15.88,3.15 C13.78,1.35 11.06,0.25 8.07,0.25 C5.08,0.25 2.35,1.35 0.25,3.16 C0.25,3.16 7.23,11.86 7.23,11.86 C7.66,12.38 8.46,12.38 8.89,11.85c "/>
+ </group>
+ <group android:name="_R_G_L_2_G" android:translateX="0.10000000000000142"
+ android:translateY="3">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="0.3" android:fillType="nonZero"
+ android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/>
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="2.6799999999999997"
+ android:translateY="7.748">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M10.14 13.85 C10.14,13.85 18.39,3.59 18.39,3.59 C15.86,1.43 12.65,0.25 9.32,0.25 C5.86,0.25 2.69,1.51 0.25,3.6 C0.25,3.6 8.48,13.86 8.48,13.86 C8.91,14.38 9.71,14.38 10.14,13.85c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="-0.3000000000000007"
+ android:translateY="2.75">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M12.3 0.25 C6.74,0.25 2.63,2.61 0.86,3.82 C0.35,4.17 0.25,4.88 0.63,5.36 C0.63,5.36 11.46,18.85 11.46,18.85 C11.88,19.38 12.69,19.38 13.12,18.85 C13.12,18.85 23.96,5.36 23.96,5.36 C24.35,4.88 24.25,4.17 23.74,3.82 C21.97,2.61 17.85,0.25 12.3,0.25c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
new file mode 100644
index 0000000..09e7360
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable">
+ <target android:name="_R_G_L_1_G_D_0_P_0"
+ android:animation="@*android:anim/ic_bluetooth_transient_animation_0"/>
+ <target android:name="_R_G_L_0_G_D_0_P_0"
+ android:animation="@*android:anim/ic_bluetooth_transient_animation_1"/>
+ <target android:name="time_group"
+ android:animation="@*android:anim/ic_bluetooth_transient_animation_2"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
new file mode 100644
index 0000000..e0f155a
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+ android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G" android:translateX="3.75"
+ android:translateY="1.4410000000000007">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M13.78 5.28 C13.78,5.28 9.03,0.53 9.03,0.53 C8.82,0.31 8.49,0.25 8.21,0.37 C7.93,0.48 7.75,0.75 7.75,1.06 C7.75,1.06 7.75,8.75 7.75,8.75 C7.75,8.75 3.78,4.78 3.78,4.78 C3.49,4.49 3.01,4.49 2.72,4.78 C2.43,5.07 2.43,5.55 2.72,5.84 C2.72,5.84 7.44,10.56 7.44,10.56 C7.44,10.56 2.72,15.28 2.72,15.28 C2.43,15.57 2.43,16.05 2.72,16.34 C3.01,16.63 3.49,16.63 3.78,16.34 C3.78,16.34 7.75,12.37 7.75,12.37 C7.75,12.37 7.75,20.06 7.75,20.06 C7.75,20.36 7.93,20.64 8.21,20.75 C8.31,20.79 8.4,20.81 8.5,20.81 C8.7,20.81 8.89,20.73 9.03,20.59 C9.03,20.59 13.78,15.84 13.78,15.84 C14.07,15.55 14.07,15.07 13.78,14.78 C13.78,14.78 9.56,10.56 9.56,10.56 C9.56,10.56 13.78,6.34 13.78,6.34 C14.07,6.05 14.07,5.57 13.78,5.28c M12.19 15.31 C12.19,15.31 9.25,18.25 9.25,18.25 C9.25,18.25 9.25,12.37 9.25,12.37 C9.25,12.37 12.19,15.31 12.19,15.31c M9.25 8.75 C9.25,8.75 9.25,2.87 9.25,2.87 C9.25,2.87 12.19,5.81 12.19,5.81 C12.19,5.81 9.25,8.75 9.25,8.75c "/>
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="3.75"
+ android:translateY="1.4410000000000007">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M15.25 9.56 C14.7,9.56 14.25,10.01 14.25,10.56 C14.25,11.11 14.7,11.56 15.25,11.56 C15.8,11.56 16.25,11.11 16.25,10.56 C16.25,10.01 15.8,9.56 15.25,9.56c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="3.75"
+ android:translateY="1.4410000000000007">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M1.25 9.56 C0.7,9.56 0.25,10.01 0.25,10.56 C0.25,11.11 0.7,11.56 1.25,11.56 C1.8,11.56 2.25,11.11 2.25,10.56 C2.25,10.01 1.8,9.56 1.25,9.56c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
+
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
new file mode 100644
index 0000000..1317f66
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable">
+ <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/>
+ <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/>
+ <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/>
+ <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
new file mode 100644
index 0000000..6b87f63
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
+ android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G" android:translateX="2"
+ android:translateY="1.552999999999999">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M8.5 10.95 C8.5,11.77 9.17,12.45 10,12.45 C10.83,12.45 11.5,11.77 11.5,10.95 C11.5,10.12 10.83,9.45 10,9.45 C9.17,9.45 8.5,10.12 8.5,10.95c "/>
+ <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M13.01 15.01 C13.3,15.31 13.77,15.31 14.07,15.01 C15.15,13.93 15.75,12.48 15.75,10.95 C15.75,9.41 15.15,7.97 14.07,6.88 C11.82,4.64 8.18,4.64 5.94,6.88 C4.85,7.97 4.25,9.41 4.25,10.95 C4.25,12.48 4.85,13.93 5.94,15.01 C6.08,15.16 6.27,15.23 6.47,15.23 C6.66,15.23 6.85,15.16 6.99,15.01 C7.29,14.72 7.29,14.25 6.99,13.95 C6.19,13.15 5.75,12.08 5.75,10.95 C5.75,9.81 6.19,8.74 6.99,7.94 C8.65,6.28 11.35,6.28 13.01,7.94 C13.81,8.74 14.25,9.81 14.25,10.95 C14.25,12.08 13.81,13.15 13.01,13.95 C12.71,14.25 12.71,14.72 13.01,15.01c "/>
+ <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M16.36 18.06 C16.56,18.06 16.75,17.99 16.89,17.84 C18.74,16 19.75,13.55 19.75,10.95 C19.75,8.34 18.74,5.89 16.89,4.05 C13.09,0.25 6.91,0.25 3.11,4.05 C1.26,5.89 0.25,8.34 0.25,10.95 C0.25,13.55 1.26,16 3.11,17.84 C3.4,18.13 3.87,18.13 4.17,17.84 C4.46,17.55 4.46,17.07 4.17,16.78 C2.61,15.22 1.75,13.15 1.75,10.95 C1.75,8.74 2.61,6.67 4.17,5.11 C7.38,1.9 12.62,1.9 15.83,5.11 C17.39,6.67 18.25,8.74 18.25,10.95 C18.25,13.15 17.39,15.22 15.83,16.78 C15.54,17.07 15.54,17.55 15.83,17.84 C15.98,17.99 16.17,18.06 16.36,18.06c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
new file mode 100644
index 0000000..ae64e56
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable">
+ <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/>
+ <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/>
+ <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/>
+ <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/>
+ <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/>
+ <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/>
+ <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/>
+ <target android:name="_R_G_L_0_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_7"/>
+ <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_8"/>
+</animated-vector>
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
new file mode 100644
index 0000000..4a2505a
--- /dev/null
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp" android:width="24dp" android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_4_G" android:translateX="0.10500000000000043"
+ android:translateY="1.7490000000000006">
+ <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.9,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c M11.9 17.89 C11.9,17.89 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 11.9,17.89 11.9,17.89c "/>
+ </group>
+ <group android:name="_R_G_L_3_G" android:translateX="0.10500000000000043"
+ android:translateY="1.75">
+ <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 7.48,14.87 7.48,14.87 C7.48,14.87 7.48,14.87 7.48,14.87 C7.48,14.87 8,15.51 8,15.51 C8,15.51 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.9,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 15.79,15.51 15.79,15.51 C15.79,15.51 16.31,14.87 16.31,14.87 C16.31,14.87 16.31,14.87 16.31,14.87 C16.31,14.87 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c M15.67 13.29 C15.57,13.15 15.47,13.01 15.36,12.88 C14.54,11.88 13.3,11.24 11.9,11.24 C10.5,11.24 9.25,11.88 8.43,12.88 C8.32,13.01 8.22,13.15 8.12,13.29 C8.12,13.29 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 15.67,13.29 15.67,13.29c "/>
+ </group>
+ <group android:name="_R_G_L_2_G" android:translateX="0.10500000000000043"
+ android:translateY="1.7490000000000006">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M11.89 0.25 C7.65,0.25 3.77,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.89,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.48,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.89,0.25c M17.61 10.93 C17.5,10.8 17.4,10.66 17.28,10.54 C15.92,9.12 14.01,8.24 11.89,8.24 C9.77,8.24 7.86,9.12 6.51,10.54 C6.39,10.66 6.29,10.8 6.18,10.92 C6.18,10.92 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.89,1.75 C15.6,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 17.61,10.93 17.61,10.93c "/>
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="0.10500000000000043"
+ android:translateY="1.7490000000000006">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 3.2,9.66 3.2,9.66 C3.2,9.66 3.2,9.66 3.2,9.66 C3.2,9.66 11.12,19.31 11.12,19.31 C11.52,19.8 12.27,19.8 12.67,19.31 C12.67,19.31 20.6,9.66 20.6,9.66 C20.6,9.66 20.59,9.66 20.59,9.66 C20.59,9.66 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c M19.6 8.5 C19.51,8.41 19.43,8.32 19.34,8.23 C17.41,6.37 14.8,5.24 11.9,5.24 C8.99,5.24 6.38,6.38 4.45,8.23 C4.36,8.32 4.28,8.41 4.19,8.51 C4.19,8.51 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 19.6,8.5 19.6,8.5c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="0.10500000000000043"
+ android:translateY="1.75">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000"
+ android:fillAlpha="1" android:fillType="nonZero"
+ android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.52,19.8 12.27,19.8 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+</vector>
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index fe92d45..10387f1 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7213,6 +7213,9 @@
// confirmation)
DIALOG_DELETE_SIM_PROGRESS = 1714;
+ // Settings > Apps and notifications > Notifications > Gentle notifications
+ GENTLE_NOTIFICATIONS_SCREEN = 1715;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 3d392c7..80b0375 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -211,25 +211,26 @@
* Gets the {@link AutofillId} of the autofillable nodes in the {@code structure}.
*/
@NonNull
- static ArraySet<AutofillId> getAutofillableIds(@NonNull AssistStructure structure) {
- final ArraySet<AutofillId> ids = new ArraySet<>();
+ static ArrayList<AutofillId> getAutofillIds(@NonNull AssistStructure structure,
+ boolean autofillableOnly) {
+ final ArrayList<AutofillId> ids = new ArrayList<>();
final int size = structure.getWindowNodeCount();
for (int i = 0; i < size; i++) {
final WindowNode node = structure.getWindowNodeAt(i);
- addAutofillableIds(node.getRootViewNode(), ids);
+ addAutofillableIds(node.getRootViewNode(), ids, autofillableOnly);
}
return ids;
}
private static void addAutofillableIds(@NonNull ViewNode node,
- @NonNull ArraySet<AutofillId> ids) {
- if (node.getAutofillType() != View.AUTOFILL_TYPE_NONE) {
+ @NonNull ArrayList<AutofillId> ids, boolean autofillableOnly) {
+ if (!autofillableOnly || node.getAutofillType() != View.AUTOFILL_TYPE_NONE) {
ids.add(node.getAutofillId());
}
final int size = node.getChildCount();
for (int i = 0; i < size; i++) {
final ViewNode child = node.getChildAt(i);
- addAutofillableIds(child, ids);
+ addAutofillableIds(child, ids, autofillableOnly);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 7b97353..f35e464 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -26,7 +26,6 @@
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
-import static android.view.autofill.Helper.toList;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.autofill.Helper.getNumericValue;
@@ -283,7 +282,7 @@
* on autofilling the app.
*/
@GuardedBy("mLock")
- private ArraySet<AutofillId> mAugmentedAutofillableIds;
+ private ArrayList<AutofillId> mAugmentedAutofillableIds;
/**
* When {@code true}, the session was created only to handle Augmented Autofill requests (i.e.,
@@ -336,6 +335,12 @@
return;
}
+ final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure,
+ /* autofillableOnly= */false);
+ for (int i = 0; i < ids.size(); i++) {
+ ids.get(i).setSessionId(Session.this.id);
+ }
+
// Flags used to start the session.
int flags = structure.getFlags();
@@ -2259,6 +2264,7 @@
+ id + " destroyed");
return;
}
+ id.setSessionId(this.id);
if (sVerbose) {
Slog.v(TAG, "updateLocked(" + this.id + "): id=" + id + ", action="
+ actionAsString(action) + ", flags=" + flags);
@@ -2528,14 +2534,14 @@
}
private void notifyUnavailableToClient(int sessionFinishedState,
- @Nullable ArraySet<AutofillId> autofillableIds) {
+ @Nullable ArrayList<AutofillId> autofillableIds) {
synchronized (mLock) {
if (mCurrentViewId == null) return;
try {
if (mHasCallback) {
mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinishedState);
} else if (sessionFinishedState != 0) {
- mClient.setSessionFinished(sessionFinishedState, toList(autofillableIds));
+ mClient.setSessionFinished(sessionFinishedState, autofillableIds);
}
} catch (RemoteException e) {
Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e);
@@ -2661,10 +2667,10 @@
final FillContext context = getFillContextByRequestIdLocked(requestId);
- final ArraySet<AutofillId> autofillableIds;
+ final ArrayList<AutofillId> autofillableIds;
if (context != null) {
final AssistStructure structure = context.getStructure();
- autofillableIds = Helper.getAutofillableIds(structure);
+ autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */true);
} else {
Slog.w(TAG, "processNullResponseLocked(): no context for req " + requestId);
autofillableIds = null;
@@ -2770,7 +2776,9 @@
remoteService.getComponentName().getPackageName());
mAugmentedRequestsLogs.add(log);
- remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, mCurrentViewId,
+ final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId);
+
+ remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
currentValue);
if (mAugmentedAutofillDestroyer == null) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 843aa74..73f5cb8 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -297,6 +297,9 @@
// First apply the unconditional transformations (if any) to the templates.
final ArrayList<Pair<Integer, InternalTransformation>> transformations =
customDescription.getTransformations();
+ if (sVerbose) {
+ Slog.v(TAG, "applyCustomDescription(): transformations = " + transformations);
+ }
if (transformations != null) {
if (!InternalTransformation.batchApply(valueFinder, template, transformations)) {
Slog.w(TAG, "could not apply main transformations on custom description");
@@ -345,6 +348,10 @@
// Apply batch updates (if any).
final ArrayList<Pair<InternalValidator, BatchUpdates>> updates =
customDescription.getUpdates();
+ if (sVerbose) {
+ Slog.v(TAG, "applyCustomDescription(): view = " + customSubtitleView
+ + " updates=" + updates);
+ }
if (updates != null) {
final int size = updates.size();
if (sDebug) Slog.d(TAG, "custom description has " + size + " batch updates");
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 6573c3b..188d654 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1136,11 +1136,21 @@
public void unbindBluetoothProfileService(int bluetoothProfile,
IBluetoothProfileServiceConnection proxy) {
synchronized (mProfileServices) {
- ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
+ Integer profile = new Integer(bluetoothProfile);
+ ProfileServiceConnections psc = mProfileServices.get(profile);
if (psc == null) {
return;
}
psc.removeProxy(proxy);
+ if (psc.isEmpty()) {
+ // All prxoies are disconnected, unbind with the service.
+ try {
+ mContext.unbindService(psc);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
+ }
+ mProfileServices.remove(profile);
+ }
}
}
@@ -1298,6 +1308,10 @@
mProxies.kill();
}
+ private boolean isEmpty() {
+ return mProxies.getRegisteredCallbackCount() == 0;
+ }
+
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// remove timeout message
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 44fc45e..d52fe81 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -120,7 +120,9 @@
import com.android.server.location.PassiveProvider;
import com.android.server.location.RemoteListenerHelper;
+import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
+import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -2299,6 +2301,7 @@
private boolean mIsForegroundUid;
private Location mLastFixBroadcast;
private long mLastStatusBroadcast;
+ private Throwable mStackTrace; // for debugging only
/**
* Note: must be constructed with lock held.
@@ -2311,6 +2314,10 @@
mIsForegroundUid = isImportanceForeground(
mActivityManager.getPackageImportance(mReceiver.mCallerIdentity.mPackageName));
+ if (D && receiver.mCallerIdentity.mPid == Process.myPid()) {
+ mStackTrace = new Throwable();
+ }
+
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null) {
records = new ArrayList<>();
@@ -2361,11 +2368,26 @@
@Override
public String toString() {
- return "UpdateRecord[" + mProvider + " " + mReceiver.mCallerIdentity.mPackageName
- + "(" + mReceiver.mCallerIdentity.mUid + (mIsForegroundUid ? " foreground"
- : " background")
- + ")" + " " + mRealRequest + " "
- + mReceiver.mWorkSource + "]";
+ StringBuilder b = new StringBuilder("UpdateRecord[");
+ b.append(mProvider).append(" ");
+ b.append(mReceiver.mCallerIdentity.mPackageName);
+ b.append("(").append(mReceiver.mCallerIdentity.mUid);
+ if (mIsForegroundUid) {
+ b.append(" foreground");
+ } else {
+ b.append(" background");
+ }
+ b.append(") ");
+ b.append(mRealRequest).append(" ").append(mReceiver.mWorkSource);
+
+ if (mStackTrace != null) {
+ ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+ mStackTrace.printStackTrace(new PrintStream(tmp));
+ b.append("\n\n").append(tmp.toString()).append("\n");
+ }
+
+ b.append("]");
+ return b.toString();
}
}
@@ -3642,6 +3664,13 @@
}
}
+ if (!mIgnoreSettingsPackageWhitelist.isEmpty()) {
+ pw.println(" Bypass Whitelisted Packages:");
+ for (String packageName : mIgnoreSettingsPackageWhitelist) {
+ pw.println(" " + packageName);
+ }
+ }
+
if (mLocationFudger != null) {
pw.append(" fudger: ");
mLocationFudger.dump(fd, pw, args);
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 3a50aa8..0eb3a84 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -33,6 +33,8 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Handler;
@@ -45,7 +47,7 @@
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
-import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.service.dreams.Sandman;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -87,6 +89,7 @@
private boolean mVrHeadset;
private boolean mComputedNightMode;
private int mCarModeEnableFlags;
+ private boolean mSetupWizardComplete;
// flag set by resource, whether to enable Car dock launch when starting car mode.
private boolean mEnableCarDockLaunch = true;
@@ -196,6 +199,29 @@
}
};
+ private final ContentObserver mSetupWizardObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ // setup wizard is done now so we can unblock
+ if (setupWizardCompleteForCurrentUser()) {
+ mSetupWizardComplete = true;
+ getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
+ // update night mode
+ Context context = getContext();
+ updateNightModeFromSettings(context, context.getResources(),
+ UserHandle.getCallingUserId());
+ updateLocked(0, 0);
+ }
+ }
+ };
+
+ @Override
+ public void onSwitchUser(int userHandle) {
+ super.onSwitchUser(userHandle);
+ getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
+ verifySetupWizardCompleted();
+ }
+
@Override
public void onStart() {
final Context context = getContext();
@@ -204,6 +230,10 @@
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
+ // If setup isn't complete for this user listen for completion so we can unblock
+ // being able to send a night mode configuration change event
+ verifySetupWizardCompleted();
+
context.registerReceiver(mDockModeReceiver,
new IntentFilter(Intent.ACTION_DOCK_EVENT));
IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
@@ -262,6 +292,25 @@
context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
}
+ // Records whether setup wizard has happened or not and adds an observer for this user if not.
+ private void verifySetupWizardCompleted() {
+ final Context context = getContext();
+ final int userId = UserHandle.getCallingUserId();
+ if (!setupWizardCompleteForCurrentUser()) {
+ mSetupWizardComplete = false;
+ context.getContentResolver().registerContentObserver(
+ Secure.getUriFor(
+ Secure.USER_SETUP_COMPLETE), false, mSetupWizardObserver, userId);
+ } else {
+ mSetupWizardComplete = true;
+ }
+ }
+
+ private boolean setupWizardCompleteForCurrentUser() {
+ return Secure.getIntForUser(getContext().getContentResolver(),
+ Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1;
+ }
+
/**
* Updates the night mode setting in Settings.Global and returns if the value was successfully
* changed.
@@ -274,8 +323,12 @@
final int defaultNightMode = res.getInteger(
com.android.internal.R.integer.config_defaultNightMode);
int oldNightMode = mNightMode;
- mNightMode = Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.UI_NIGHT_MODE, defaultNightMode, userId);
+ if (mSetupWizardComplete) {
+ mNightMode = Secure.getIntForUser(context.getContentResolver(),
+ Secure.UI_NIGHT_MODE, defaultNightMode, userId);
+ } else {
+ mNightMode = defaultNightMode;
+ }
// false if night mode stayed the same, true otherwise.
return !(oldNightMode == mNightMode);
@@ -340,6 +393,10 @@
Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
return;
}
+ if (!mSetupWizardComplete) {
+ Slog.d(TAG, "Night mode cannot be changed before setup wizard completes.");
+ return;
+ }
switch (mode) {
case UiModeManager.MODE_NIGHT_NO:
case UiModeManager.MODE_NIGHT_YES:
@@ -356,8 +413,8 @@
if (mNightMode != mode) {
// Only persist setting if not in car mode
if (!mCarModeEnabled) {
- Settings.Secure.putIntForUser(getContext().getContentResolver(),
- Settings.Secure.UI_NIGHT_MODE, mode, user);
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.UI_NIGHT_MODE, mode, user);
}
mNightMode = mode;
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 7c2ea3f..30a297e 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -144,3 +144,6 @@
30064 am_on_top_resumed_gained_called (User|1|5),(Component Name|3),(Reason|3)
# The activity's onTopResumedActivityChanged(false) has been called.
30065 am_on_top_resumed_lost_called (User|1|5),(Component Name|3),(Reason|3)
+
+# An activity been add into stopping list
+30066 am_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 37c15a0..feb58a3a 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -938,7 +938,7 @@
try {
userId = getUserOrWorkProfileId(clientPackage, userId);
if (userId != mCurrentUserId) {
- final File baseDir = Environment.getDataVendorCeDirectory(userId);
+ final File baseDir = Environment.getDataVendorDeDirectory(userId);
final File faceDir = new File(baseDir, FACE_DATA_DIR);
if (!faceDir.exists()) {
if (!faceDir.mkdir()) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3abd0ba..6d01375 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -16,12 +16,15 @@
package com.android.server.display;
+import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT;
+import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
-import static android.hardware.display.DisplayManager
- .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL;
@@ -1979,6 +1982,18 @@
}
}
+ // Sometimes users can have sensitive information in system decoration windows. An app
+ // could create a virtual display with system decorations support and read the user info
+ // from the surface.
+ // We should only allow adding flag VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ // to virtual displays that are owned by the system.
+ if (callingUid != Process.SYSTEM_UID
+ && (flags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
+ if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) {
+ throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
+ }
+ }
+
final long token = Binder.clearCallingIdentity();
try {
return createVirtualDisplayInternal(callback, projection, callingUid, packageName,
@@ -2279,9 +2294,7 @@
Slog.e(TAG, "Unable to query projection service for permissions", e);
}
}
- if (mContext.checkCallingPermission(
- android.Manifest.permission.CAPTURE_VIDEO_OUTPUT)
- == PackageManager.PERMISSION_GRANTED) {
+ if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) {
return true;
}
return canProjectSecureVideo(projection);
@@ -2297,9 +2310,17 @@
Slog.e(TAG, "Unable to query projection service for permissions", e);
}
}
- return mContext.checkCallingPermission(
- android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT)
- == PackageManager.PERMISSION_GRANTED;
+ return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, "canProjectSecureVideo()");
+ }
+
+ private boolean checkCallingPermission(String permission, String func) {
+ if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ final String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid() + " requires " + permission;
+ Slog.w(TAG, msg);
+ return false;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 30d244f..6330270 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -63,7 +63,9 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
+import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManagerInternal;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
import android.os.Binder;
@@ -97,7 +99,9 @@
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.ContextThemeWrapper;
+import android.view.DisplayInfo;
import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.LayoutInflater;
@@ -300,6 +304,7 @@
final SettingsObserver mSettingsObserver;
final IWindowManager mIWindowManager;
final WindowManagerInternal mWindowManagerInternal;
+ private final DisplayManagerInternal mDisplayManagerInternal;
final HandlerCaller mCaller;
final boolean mHasFeature;
private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
@@ -432,6 +437,32 @@
final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
+ private static final class ActivityViewInfo {
+ /**
+ * {@link ClientState} where {@link android.app.ActivityView} is running.
+ */
+ private final ClientState mParentClient;
+ /**
+ * {@link Matrix} to convert screen coordinates in the embedded virtual display to
+ * screen coordinates where {@link #mParentClient} exists.
+ */
+ private final Matrix mMatrix;
+
+ ActivityViewInfo(ClientState parentClient, Matrix matrix) {
+ mParentClient = parentClient;
+ mMatrix = matrix;
+ }
+ }
+
+ /**
+ * A mapping table from virtual display IDs created for {@link android.app.ActivityView}
+ * to its parent IME client where {@link android.app.ActivityView} is running.
+ *
+ * <p>Note: this can be used only for virtual display IDs created by
+ * {@link android.app.ActivityView}.</p>
+ */
+ private SparseArray<ActivityViewInfo> mActivityViewDisplayIdToParentMap = new SparseArray<>();
+
/**
* Set once the system is ready to run third party code.
*/
@@ -510,6 +541,16 @@
EditorInfo mCurAttribute;
/**
+ * A special {@link Matrix} to convert virtual screen coordinates to the IME target display
+ * coordinates.
+ *
+ * <p>Used only while the IME client is running in a virtual display inside
+ * {@link android.app.ActivityView}. {@code null} otherwise.</p>
+ */
+ @Nullable
+ private Matrix mCurActivityViewToScreenMatrix = null;
+
+ /**
* Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
* connected to or in the process of connecting to.
*
@@ -1409,6 +1450,7 @@
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+ mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mImeDisplayValidator = displayId -> mWindowManagerInternal.shouldShowIme(displayId);
mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
@Override
@@ -1883,6 +1925,15 @@
if (cs != null) {
client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0);
clearClientSessionLocked(cs);
+
+ final int numItems = mActivityViewDisplayIdToParentMap.size();
+ for (int i = numItems - 1; i >= 0; --i) {
+ final ActivityViewInfo info = mActivityViewDisplayIdToParentMap.valueAt(i);
+ if (info.mParentClient == cs) {
+ mActivityViewDisplayIdToParentMap.removeAt(i);
+ }
+ }
+
if (mCurClient == cs) {
if (mBoundToMethod) {
mBoundToMethod = false;
@@ -1892,6 +1943,7 @@
}
}
mCurClient = null;
+ mCurActivityViewToScreenMatrix = null;
}
if (mCurFocusedWindowClient == cs) {
mCurFocusedWindowClient = null;
@@ -1927,6 +1979,7 @@
MSG_UNBIND_CLIENT, mCurSeq, unbindClientReason, mCurClient.client));
mCurClient.sessionRequested = false;
mCurClient = null;
+ mCurActivityViewToScreenMatrix = null;
hideInputMethodMenuLocked();
}
@@ -1980,7 +2033,31 @@
}
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, (session.channel != null ? session.channel.dup() : null),
- mCurId, mCurSeq);
+ mCurId, mCurSeq, mCurActivityViewToScreenMatrix);
+ }
+
+ @Nullable
+ private Matrix getActivityViewToScreenMatrixLocked(int clientDisplayId, int imeDisplayId) {
+ if (clientDisplayId == imeDisplayId) {
+ return null;
+ }
+ int displayId = clientDisplayId;
+ Matrix matrix = null;
+ while (true) {
+ final ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(displayId);
+ if (info == null) {
+ return null;
+ }
+ if (matrix == null) {
+ matrix = new Matrix(info.mMatrix);
+ } else {
+ matrix.postConcat(info.mMatrix);
+ }
+ if (info.mParentClient.selfReportedDisplayId == imeDisplayId) {
+ return matrix;
+ }
+ displayId = info.mParentClient.selfReportedDisplayId;
+ }
}
@GuardedBy("mMethodMap")
@@ -1998,7 +2075,7 @@
// party code.
return new InputBindResult(
InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
- null, null, mCurMethodId, mCurSeq);
+ null, null, mCurMethodId, mCurSeq, null);
}
if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
@@ -2037,7 +2114,10 @@
if (mCurSeq <= 0) mCurSeq = 1;
mCurClient = cs;
mCurInputContext = inputContext;
- if (cs.selfReportedDisplayId != displayIdToShowIme) {
+ mCurActivityViewToScreenMatrix =
+ getActivityViewToScreenMatrixLocked(cs.selfReportedDisplayId, displayIdToShowIme);
+ if (cs.selfReportedDisplayId != displayIdToShowIme
+ && mCurActivityViewToScreenMatrix == null) {
// CursorAnchorInfo API does not work as-is for cross-display scenario. Pretend that
// InputConnection#requestCursorUpdates() is not implemented in the application so that
// IMEs will always receive false from this API.
@@ -2064,7 +2144,7 @@
requestClientSessionLocked(cs);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
- null, null, mCurId, mCurSeq);
+ null, null, mCurId, mCurSeq, null);
} else if (SystemClock.uptimeMillis()
< (mLastBindTime+TIME_TO_RECONNECT)) {
// In this case we have connected to the service, but
@@ -2076,7 +2156,7 @@
// to see if we can get back in touch with the service.
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, mCurId, mCurSeq);
+ null, null, mCurId, mCurSeq, null);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
@@ -2115,7 +2195,7 @@
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, mCurId, mCurSeq);
+ null, null, mCurId, mCurSeq, null);
}
mCurIntent = null;
Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent);
@@ -2960,7 +3040,7 @@
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
- null, null, null, -1);
+ null, null, null, -1, null);
}
mCurFocusedWindow = windowToken;
mCurFocusedWindowSoftInputMode = softInputMode;
@@ -3387,6 +3467,88 @@
return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId);
}
+ @Override
+ public void reportActivityView(IInputMethodClient parentClient, int childDisplayId,
+ float[] matrixValues) {
+ final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(childDisplayId);
+ if (displayInfo == null) {
+ throw new IllegalArgumentException(
+ "Cannot find display for non-existent displayId: " + childDisplayId);
+ }
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != displayInfo.ownerUid) {
+ throw new SecurityException("The caller doesn't own the display.");
+ }
+
+ synchronized (mMethodMap) {
+ final ClientState cs = mClients.get(parentClient.asBinder());
+ if (cs == null) {
+ return;
+ }
+
+ // null matrixValues means that the entry needs to be removed.
+ if (matrixValues == null) {
+ final ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(childDisplayId);
+ if (info == null) {
+ return;
+ }
+ if (info.mParentClient != cs) {
+ throw new SecurityException("Only the owner client can clear"
+ + " ActivityViewGeometry for display #" + childDisplayId);
+ }
+ mActivityViewDisplayIdToParentMap.remove(childDisplayId);
+ return;
+ }
+
+ ActivityViewInfo info = mActivityViewDisplayIdToParentMap.get(childDisplayId);
+ if (info != null && info.mParentClient != cs) {
+ throw new InvalidParameterException("Display #" + childDisplayId
+ + " is already registered by " + info.mParentClient);
+ }
+ if (info == null) {
+ if (!mWindowManagerInternal.isUidAllowedOnDisplay(childDisplayId, cs.uid)) {
+ throw new SecurityException(cs + " cannot access to display #"
+ + childDisplayId);
+ }
+ info = new ActivityViewInfo(cs, new Matrix());
+ mActivityViewDisplayIdToParentMap.put(childDisplayId, info);
+ }
+ info.mMatrix.setValues(matrixValues);
+
+ if (mCurClient == null || mCurClient.curSession == null) {
+ return;
+ }
+
+ Matrix matrix = null;
+ int displayId = mCurClient.selfReportedDisplayId;
+ boolean needToNotify = false;
+ while (true) {
+ needToNotify |= (displayId == childDisplayId);
+ final ActivityViewInfo next = mActivityViewDisplayIdToParentMap.get(displayId);
+ if (next == null) {
+ break;
+ }
+ if (matrix == null) {
+ matrix = new Matrix(next.mMatrix);
+ } else {
+ matrix.postConcat(next.mMatrix);
+ }
+ if (next.mParentClient.selfReportedDisplayId == mCurTokenDisplayId) {
+ if (needToNotify) {
+ final float[] values = new float[9];
+ matrix.getValues(values);
+ try {
+ mCurClient.client.updateActivityViewToScreenMatrix(mCurSeq, values);
+ } catch (RemoteException e) {
+ }
+ }
+ break;
+ }
+ displayId = info.mParentClient.selfReportedDisplayId;
+ }
+ }
+ }
+
@BinderThread
private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index e0b8e71..3dd7304 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1619,7 +1619,7 @@
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
null, null, data.mCurrentInputMethodInfo.getId(),
- clientInfo.mBindingSequence);
+ clientInfo.mBindingSequence, null);
case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
clientInfo.mBindingSequence++;
@@ -1642,7 +1642,7 @@
clientInfo.mInputMethodSession,
clientInfo.mWriteChannel.dup(),
data.mCurrentInputMethodInfo.getId(),
- clientInfo.mBindingSequence);
+ clientInfo.mBindingSequence, null);
case InputMethodClientState.UNREGISTERED:
Slog.e(TAG, "The client is already unregistered.");
return InputBindResult.INVALID_CLIENT;
@@ -1701,6 +1701,13 @@
@BinderThread
@Override
+ public void reportActivityView(IInputMethodClient parentClient, int childDisplayId,
+ float[] matrixValues) {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@Nullable FileDescriptor err, String[] args, @Nullable ShellCallback callback,
ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6f8439b..a64ae9c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -14996,12 +14996,14 @@
final int installReason;
@Nullable
MultiPackageInstallParams mParentInstallParams;
+ final long requiredInstalledVersionCode;
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, String volumeUuid,
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
- PackageParser.SigningDetails signingDetails, int installReason) {
+ PackageParser.SigningDetails signingDetails, int installReason,
+ long requiredInstalledVersionCode) {
super(user);
this.origin = origin;
this.move = move;
@@ -15015,6 +15017,7 @@
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
this.signingDetails = signingDetails;
this.installReason = installReason;
+ this.requiredInstalledVersionCode = requiredInstalledVersionCode;
}
InstallParams(ActiveInstallSession activeInstallSession) {
@@ -15045,6 +15048,8 @@
whitelistedRestrictedPermissions = activeInstallSession.getSessionParams()
.whitelistedRestrictedPermissions;
signingDetails = activeInstallSession.getSigningDetails();
+ requiredInstalledVersionCode = activeInstallSession.getSessionParams()
+ .requiredInstalledVersionCode;
}
@Override
@@ -15073,6 +15078,23 @@
}
}
+ if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
+ if (dataOwnerPkg == null) {
+ Slog.w(TAG, "Required installed version code was "
+ + requiredInstalledVersionCode
+ + " but package is not installed");
+ return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
+ }
+
+ if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
+ Slog.w(TAG, "Required installed version code was "
+ + requiredInstalledVersionCode
+ + " but actual installed version is "
+ + dataOwnerPkg.getLongVersionCode());
+ return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
+ }
+ }
+
if (dataOwnerPkg != null) {
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
dataOwnerPkg.applicationInfo.flags)) {
@@ -15199,6 +15221,8 @@
loc = installLocationPolicy(pkgLite);
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+ } else if (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) {
+ ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
} else if (!onInt) {
// Override install location with flags
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
@@ -23311,7 +23335,7 @@
installerPackageName, volumeUuid, null /*verificationInfo*/, user,
packageAbiOverride, null /*grantedPermissions*/,
null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN,
- PackageManager.INSTALL_REASON_UNKNOWN);
+ PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST);
params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
@@ -23937,6 +23961,18 @@
}
@Override
+ public int getTargetSdkVersionForPackage(String packageName)
+ throws RemoteException {
+ int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+ ApplicationInfo info = getApplicationInfo(packageName, 0, callingUser);
+ if (info == null) {
+ throw new RemoteException(
+ "Couldn't get ApplicationInfo for package " + packageName);
+ }
+ return info.targetSdkVersion;
+ }
+
+ @Override
public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames)
throws RemoteException {
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index bb9f674..170d085 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -163,6 +163,22 @@
continue;
}
long activeVersion = activePackage.applicationInfo.longVersionCode;
+ if (session.params.requiredInstalledVersionCode
+ != PackageManager.VERSION_CODE_HIGHEST) {
+ if (activeVersion != session.params.requiredInstalledVersionCode) {
+ session.setStagedSessionFailed(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Installed version of APEX package " + newPackage.packageName
+ + " does not match required. Active version: " + activeVersion
+ + " required: " + session.params.requiredInstalledVersionCode);
+
+ if (!mApexManager.abortActiveSession()) {
+ Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
+ }
+ return false;
+ }
+ }
+
boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
session.params.installFlags, activePackage.applicationInfo.flags);
if (activeVersion > newPackage.versionCode && !allowsDowngrade) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 1682858..da88ec5 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -50,6 +50,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.util.ArraySet;
import android.util.IntArray;
import android.util.Log;
import android.util.SparseBooleanArray;
@@ -69,11 +70,9 @@
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
@@ -107,15 +106,9 @@
@GuardedBy("mLock")
private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();
- // Package rollback data for rollback-enabled installs that have not yet
- // been committed. Maps from sessionId to rollback data.
+ // Package rollback data for rollbacks we are in the process of enabling.
@GuardedBy("mLock")
- private final Map<Integer, RollbackData> mPendingRollbacks = new HashMap<>();
-
- // Map from child session id's for enabled rollbacks to their
- // corresponding parent session ids.
- @GuardedBy("mLock")
- private final Map<Integer, Integer> mChildSessions = new HashMap<>();
+ private final Set<NewRollback> mNewRollbacks = new ArraySet<>();
// The list of all rollbacks, including available and committed rollbacks.
// This list is null until the rollback data has been loaded.
@@ -136,7 +129,6 @@
// No need for guarding with lock because value is only accessed in handler thread.
private long mRelativeBootTime = calculateRelativeBootTime();
-
RollbackManagerServiceImpl(Context context) {
mContext = context;
// Note that we're calling onStart here because this object is only constructed on
@@ -247,9 +239,20 @@
}, filter, null, getHandler());
}
+ /**
+ * This method posts a blocking call to the handler thread, so it should not be called from
+ * that same thread.
+ * @throws {@link IllegalStateException} if called from {@link #mHandlerThread}
+ */
@Override
public ParceledListSlice getAvailableRollbacks() {
enforceManageRollbacks("getAvailableRollbacks");
+ if (Thread.currentThread().equals(mHandlerThread)) {
+ Log.wtf(TAG, "Calling getAvailableRollbacks from mHandlerThread "
+ + "causes a deadlock");
+ throw new IllegalStateException("Cannot call RollbackManager#getAvailableRollbacks "
+ + "from the handler thread!");
+ }
// Wait for the handler thread to get the list of available rollbacks
// to get the most up-to-date results. This is intended to reduce test
@@ -357,31 +360,6 @@
return;
}
- // Verify the RollbackData is up to date with what's installed on
- // device.
- // TODO: We assume that between now and the time we commit the
- // downgrade install, the currently installed package version does not
- // change. This is not safe to assume, particularly in the case of a
- // rollback racing with a roll-forward fix of a buggy package.
- // Figure out how to ensure we don't commit the rollback if
- // roll forward happens at the same time.
- for (PackageRollbackInfo info : data.info.getPackages()) {
- VersionedPackage installedVersion = getInstalledPackageVersion(info.getPackageName());
- if (installedVersion == null) {
- // TODO: Test this case
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
- "Package to roll back is not installed");
- return;
- }
-
- if (!packageVersionsEqual(info.getVersionRolledBackFrom(), installedVersion)) {
- // TODO: Test this case
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
- "Package version to roll back not installed.");
- return;
- }
- }
-
// Get a context for the caller to use to install the downgraded
// version of the package.
Context context = null;
@@ -420,6 +398,8 @@
}
}
params.setRequestDowngrade(true);
+ params.setRequiredInstalledVersionCode(
+ info.getVersionRolledBackFrom().getLongVersionCode());
if (data.isStaged()) {
params.setStaged();
}
@@ -847,7 +827,6 @@
// TODO: It would be nice if package manager or package installer told
// us the session directly, rather than have to search for it
// ourselves.
- PackageInstaller.SessionInfo session = null;
// getAllSessions only returns sessions for the associated user.
// Create a context with the right user so we can find the matching
@@ -858,7 +837,8 @@
return false;
}
- int parentSessionId = -1;
+ PackageInstaller.SessionInfo parentSession = null;
+ PackageInstaller.SessionInfo packageSession = null;
PackageInstaller installer = context.getPackageManager().getPackageInstaller();
for (PackageInstaller.SessionInfo info : installer.getAllSessions()) {
if (info.isMultiPackage()) {
@@ -866,21 +846,21 @@
PackageInstaller.SessionInfo child = installer.getSessionInfo(childId);
if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) {
// TODO: Check we only have one matching session?
- parentSessionId = info.getSessionId();
- session = child;
+ parentSession = info;
+ packageSession = child;
break;
}
}
} else if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) {
// TODO: Check we only have one matching session?
- parentSessionId = info.getSessionId();
- session = info;
+ parentSession = info;
+ packageSession = info;
break;
}
}
- if (session == null) {
- Log.e(TAG, "Unable to find session id for enabled rollback.");
+ if (parentSession == null || packageSession == null) {
+ Log.e(TAG, "Unable to find session for enabled rollback.");
return false;
}
@@ -892,7 +872,7 @@
ensureRollbackDataLoadedLocked();
for (int i = 0; i < mRollbacks.size(); ++i) {
RollbackData data = mRollbacks.get(i);
- if (data.apkSessionId == parentSessionId) {
+ if (data.apkSessionId == parentSession.getSessionId()) {
rd = data;
break;
}
@@ -905,7 +885,7 @@
PackageParser.PackageLite newPackage = null;
try {
newPackage = PackageParser.parsePackageLite(
- new File(session.resolvedBaseCodePath), 0);
+ new File(packageSession.resolvedBaseCodePath), 0);
} catch (PackageParser.PackageParserException e) {
Log.e(TAG, "Unable to parse new package", e);
return false;
@@ -923,16 +903,32 @@
return false;
}
- return enableRollbackForSession(session, installedUsers, true);
+ NewRollback newRollback;
+ synchronized (mLock) {
+ // See if we already have a NewRollback that contains this package
+ // session. If not, create a NewRollback for the parent session
+ // that we will use for all the packages in the session.
+ newRollback = getNewRollbackForPackageSessionLocked(packageSession.getSessionId());
+ if (newRollback == null) {
+ newRollback = createNewRollbackLocked(parentSession);
+ mNewRollbacks.add(newRollback);
+ }
+ }
+
+ return enableRollbackForPackageSession(newRollback.data, packageSession,
+ installedUsers, /* snapshotUserData*/ true);
}
/**
* Do code and userdata backups to enable rollback of the given session.
* In case of multiPackage sessions, <code>session</code> should be one of
* the child sessions, not the parent session.
+ *
+ * @return true on success, false on failure.
*/
- private boolean enableRollbackForSession(PackageInstaller.SessionInfo session,
- @NonNull int[] installedUsers, boolean snapshotUserData) {
+ private boolean enableRollbackForPackageSession(RollbackData data,
+ PackageInstaller.SessionInfo session, @NonNull int[] installedUsers,
+ boolean snapshotUserData) {
// TODO: Don't attempt to enable rollback for split installs.
final int installFlags = session.installFlags;
if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
@@ -987,41 +983,14 @@
VersionedPackage installedVersion = new VersionedPackage(packageName,
pkgInfo.getLongVersionCode());
- PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion,
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(
+ newVersion, installedVersion,
new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
isApex, IntArray.wrap(installedUsers),
new SparseLongArray() /* ceSnapshotInodes */);
- RollbackData data;
- try {
- int childSessionId = session.getSessionId();
- int parentSessionId = session.getParentSessionId();
- if (parentSessionId == PackageInstaller.SessionInfo.INVALID_ID) {
- parentSessionId = childSessionId;
- }
-
- synchronized (mLock) {
- // TODO: no need to add to mChildSessions if childSessionId is
- // the same as parentSessionId.
- mChildSessions.put(childSessionId, parentSessionId);
- data = mPendingRollbacks.get(parentSessionId);
- if (data == null) {
- int rollbackId = allocateRollbackIdLocked();
- if (session.isStaged()) {
- data = mRollbackStore.createStagedRollback(rollbackId, parentSessionId);
- } else {
- data = mRollbackStore.createNonStagedRollback(rollbackId);
- }
- mPendingRollbacks.put(parentSessionId, data);
- }
- data.info.getPackages().add(info);
- }
- } catch (IOException e) {
- Log.e(TAG, "Unable to create rollback for " + packageName, e);
- return false;
- }
if (snapshotUserData && !isApex) {
- mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), info);
+ mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), packageRollbackInfo);
}
try {
@@ -1036,6 +1005,10 @@
Log.e(TAG, "Unable to copy package for rollback for " + packageName, e);
return false;
}
+
+ synchronized (mLock) {
+ data.info.getPackages().add(packageRollbackInfo);
+ }
return true;
}
@@ -1106,8 +1079,14 @@
return;
}
+ NewRollback newRollback;
+ synchronized (mLock) {
+ newRollback = createNewRollbackLocked(session);
+ }
+
if (!session.isMultiPackage()) {
- if (!enableRollbackForSession(session, new int[0], false)) {
+ if (!enableRollbackForPackageSession(newRollback.data, session,
+ new int[0], /* snapshotUserData */ false)) {
Log.e(TAG, "Unable to enable rollback for session: " + sessionId);
result.offer(false);
return;
@@ -1121,7 +1100,8 @@
result.offer(false);
return;
}
- if (!enableRollbackForSession(childSession, new int[0], false)) {
+ if (!enableRollbackForPackageSession(newRollback.data, childSession,
+ new int[0], /* snapshotUserData */ false)) {
Log.e(TAG, "Unable to enable rollback for session: " + sessionId);
result.offer(false);
return;
@@ -1129,8 +1109,7 @@
}
}
- completeEnableRollback(sessionId, true);
- result.offer(true);
+ result.offer(completeEnableRollback(newRollback, true) != null);
});
try {
@@ -1261,9 +1240,19 @@
@Override
public void onFinished(int sessionId, boolean success) {
- RollbackData rollback = completeEnableRollback(sessionId, success);
- if (rollback != null && !rollback.isStaged()) {
- makeRollbackAvailable(rollback);
+ NewRollback newRollback;
+ synchronized (mLock) {
+ newRollback = getNewRollbackForPackageSessionLocked(sessionId);
+ if (newRollback != null) {
+ mNewRollbacks.remove(newRollback);
+ }
+ }
+
+ if (newRollback != null) {
+ RollbackData rollback = completeEnableRollback(newRollback, success);
+ if (rollback != null && !rollback.isStaged()) {
+ makeRollbackAvailable(rollback);
+ }
}
}
}
@@ -1273,25 +1262,22 @@
* This should be called after rollback has been enabled for all packages
* in the rollback. It does not make the rollback available yet.
*
- * @return the rollback data for a successfully enable-completed rollback.
+ * @return the rollback data for a successfully enable-completed rollback,
+ * or null on error.
*/
- private RollbackData completeEnableRollback(int sessionId, boolean success) {
- RollbackData data = null;
- synchronized (mLock) {
- Integer parentSessionId = mChildSessions.remove(sessionId);
- if (parentSessionId != null) {
- sessionId = parentSessionId;
- }
-
- data = mPendingRollbacks.remove(sessionId);
- }
-
- if (data == null) {
+ private RollbackData completeEnableRollback(NewRollback newRollback, boolean success) {
+ RollbackData data = newRollback.data;
+ if (!success) {
+ // The install session was aborted, clean up the pending install.
+ deleteRollback(data);
return null;
}
- if (!success) {
- // The install session was aborted, clean up the pending install.
+ // It's safe to access data.info outside a synchronized block because
+ // this is running on the handler thread and all changes to the
+ // data.info occur on the handler thread.
+ if (data.info.getPackages().size() != newRollback.packageSessionIds.length) {
+ Log.e(TAG, "Failed to enable rollback for all packages in session.");
deleteRollback(data);
return null;
}
@@ -1375,7 +1361,7 @@
}
@GuardedBy("mLock")
- private int allocateRollbackIdLocked() throws IOException {
+ private int allocateRollbackIdLocked() {
int n = 0;
int rollbackId;
do {
@@ -1386,7 +1372,7 @@
}
} while (n++ < 32);
- throw new IOException("Failed to allocate rollback ID");
+ throw new IllegalStateException("Failed to allocate rollback ID");
}
private void deleteRollback(RollbackData rollbackData) {
@@ -1461,4 +1447,60 @@
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
}
}
+
+ private static class NewRollback {
+ public final RollbackData data;
+
+ /**
+ * Session ids for all packages in the install.
+ * For multi-package sessions, this is the list of child session ids.
+ * For normal sessions, this list is a single element with the normal
+ * session id.
+ */
+ public final int[] packageSessionIds;
+
+ NewRollback(RollbackData data, int[] packageSessionIds) {
+ this.data = data;
+ this.packageSessionIds = packageSessionIds;
+ }
+ }
+
+ NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
+ int rollbackId = allocateRollbackIdLocked();
+ final RollbackData data;
+ int parentSessionId = parentSession.getSessionId();
+
+ if (parentSession.isStaged()) {
+ data = mRollbackStore.createStagedRollback(rollbackId, parentSessionId);
+ } else {
+ data = mRollbackStore.createNonStagedRollback(rollbackId);
+ }
+
+ int[] packageSessionIds;
+ if (parentSession.isMultiPackage()) {
+ packageSessionIds = parentSession.getChildSessionIds();
+ } else {
+ packageSessionIds = new int[]{parentSessionId};
+ }
+
+ return new NewRollback(data, packageSessionIds);
+ }
+
+ /**
+ * Returns the NewRollback associated with the given package session.
+ * Returns null if no NewRollback is found for the given package
+ * session.
+ */
+ NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
+ // We expect mNewRollbacks to be a very small list; linear search
+ // should be plenty fast.
+ for (NewRollback newRollbackData : mNewRollbacks) {
+ for (int id : newRollbackData.packageSessionIds) {
+ if (id == packageSessionId) {
+ return newRollbackData;
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 2cfa465..8a26368c 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -194,7 +194,7 @@
* Creates a new RollbackData instance for a non-staged rollback with
* backupDir assigned.
*/
- RollbackData createNonStagedRollback(int rollbackId) throws IOException {
+ RollbackData createNonStagedRollback(int rollbackId) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
return new RollbackData(rollbackId, backupDir, -1);
}
@@ -203,8 +203,7 @@
* Creates a new RollbackData instance for a staged rollback with
* backupDir assigned.
*/
- RollbackData createStagedRollback(int rollbackId, int stagedSessionId)
- throws IOException {
+ RollbackData createStagedRollback(int rollbackId, int stagedSessionId) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
return new RollbackData(rollbackId, backupDir, stagedSessionId);
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 0891ba4..9decb58 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -965,16 +965,13 @@
* @param info
* */
private void startTraces(WindowingModeTransitionInfo info) {
- if (info == null) {
+ if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER) || info == null
+ || info.launchTraceActive) {
return;
}
- int transitionType = getTransitionType(info);
- if (!info.launchTraceActive && transitionType == TYPE_TRANSITION_WARM_LAUNCH
- || transitionType == TYPE_TRANSITION_COLD_LAUNCH) {
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
- + info.launchedActivity.packageName, 0);
- info.launchTraceActive = true;
- }
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
+ + info.launchedActivity.packageName, 0);
+ info.launchTraceActive = true;
}
private void stopLaunchTrace(WindowingModeTransitionInfo info) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 8a834c8..fe99fd2 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1822,7 +1822,8 @@
prev.setDeferHidingClient(false);
// If we were visible then resumeTopActivities will release resources before
// stopping.
- addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */);
+ addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */,
+ "completePauseLocked");
}
} else {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev);
@@ -1883,8 +1884,11 @@
mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
}
- private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
+ private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed,
+ String reason) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
+ EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, r.mUserId,
+ System.identityHashCode(r), r.shortComponentName, reason);
mStackSupervisor.mStoppingActivities.add(r);
}
@@ -2433,7 +2437,7 @@
case PAUSING:
case PAUSED:
addToStopping(r, true /* scheduleIdle */,
- canEnterPictureInPicture /* idleDelayed */);
+ canEnterPictureInPicture /* idleDelayed */, "makeInvisible");
break;
default:
@@ -3051,21 +3055,7 @@
ActivityOptions.abort(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityInNextFocusableStack: " + reason + ", go home");
- if (isActivityTypeHome()) {
- // resumeTopActivityUncheckedLocked has been prevented to run recursively. Post a
- // runnable to resume home since we are currently in the process of resuming top
- // activity in home stack.
- // See {@link #mInResumeTopActivity}.
- mService.mH.post(
- () -> {
- synchronized (mService.mGlobalLock) {
- mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
- }
- });
- return true;
- } else {
- return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
- }
+ return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
}
/** Returns the position the input task should be placed in this stack. */
@@ -4098,7 +4088,8 @@
if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
&& next != null && !next.nowVisible && !isFloating) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
- addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */);
+ addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */,
+ "finishCurrentActivityLocked");
}
if (DEBUG_STATES) Slog.v(TAG_STATES,
"Moving to STOPPING: "+ r + " (finish requested)");
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b6840fa..e7e34bb 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -160,6 +160,10 @@
private int mCallingUid;
private ActivityOptions mOptions;
+ // If it is true, background activity can only be started in an existing task that contains
+ // an activity with same uid.
+ private boolean mRestrictedBgActivity;
+
private int mLaunchMode;
private boolean mLaunchTaskBehind;
private int mLaunchFlags;
@@ -455,6 +459,7 @@
mIntent = starter.mIntent;
mCallingUid = starter.mCallingUid;
mOptions = starter.mOptions;
+ mRestrictedBgActivity = starter.mRestrictedBgActivity;
mLaunchTaskBehind = starter.mLaunchTaskBehind;
mLaunchFlags = starter.mLaunchFlags;
@@ -551,7 +556,8 @@
mLastStartActivityTimeMs = System.currentTimeMillis();
mLastStartActivityRecord[0] = r;
mLastStartActivityResult = startActivity(r, sourceRecord, voiceSession, voiceInteractor,
- startFlags, doResume, options, inTask, mLastStartActivityRecord);
+ startFlags, doResume, options, inTask, mLastStartActivityRecord,
+ false /* restrictedBgActivity */);
mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult,
mLastStartActivityRecord[0]);
return mLastStartActivityResult;
@@ -760,22 +766,17 @@
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
- boolean abortBackgroundStart = false;
+ boolean restrictedBgActivity = false;
if (!abort) {
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"shouldAbortBackgroundActivityStart");
- abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid,
+ restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
originatingPendingIntent, allowBackgroundActivityStart, intent);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
- abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled());
- // TODO: remove this toast after feature development is done
- if (abortBackgroundStart) {
- showBackgroundActivityBlockedToast(abort, callingPackage);
- }
}
// Merge the two options bundles, while realCallerOptions takes precedence.
@@ -918,8 +919,10 @@
|| stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
realCallingPid, realCallingUid, "Activity start")) {
- mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
- sourceRecord, startFlags, stack, callerApp));
+ if (!restrictedBgActivity) {
+ mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
+ sourceRecord, startFlags, stack, callerApp));
+ }
ActivityOptions.abort(checkedOptions);
return ActivityManager.START_SWITCHES_CANCELED;
}
@@ -929,7 +932,7 @@
mController.doPendingActivityLaunches(false);
final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
- true /* doResume */, checkedOptions, inTask, outActivity);
+ true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);
mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]);
return res;
}
@@ -1401,13 +1404,13 @@
private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
- ActivityRecord[] outActivity) {
+ ActivityRecord[] outActivity, boolean restrictedBgActivity) {
int result = START_CANCELED;
final ActivityStack startedActivityStack;
try {
mService.mWindowManager.deferSurfaceLayout();
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
- startFlags, doResume, options, inTask, outActivity);
+ startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
} finally {
final ActivityStack currentStack = r.getActivityStack();
startedActivityStack = currentStack != null ? currentStack : mTargetStack;
@@ -1443,14 +1446,40 @@
return result;
}
+ /**
+ * Return true if background activity is really aborted.
+ *
+ * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
+ */
+ private boolean handleBackgroundActivityAbort(ActivityRecord r) {
+ // TODO(b/131747138): Remove toast and refactor related code in Q release.
+ boolean abort = !mService.isBackgroundActivityStartsEnabled();
+ showBackgroundActivityBlockedToast(abort, r.launchedFromPackage);
+ if (!abort) {
+ return false;
+ }
+ ActivityRecord resultRecord = r.resultTo;
+ String resultWho = r.resultWho;
+ int requestCode = r.requestCode;
+ if (resultRecord != null) {
+ ActivityStack resultStack = resultRecord.getActivityStack();
+ resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
+ RESULT_CANCELED, null);
+ }
+ // We pretend to the caller that it was really started to make it backward compatible, but
+ // they will just get a cancel result.
+ ActivityOptions.abort(r.pendingOptions);
+ return true;
+ }
+
// Note: This method should only be called from {@link startActivity}.
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
- ActivityRecord[] outActivity) {
-
+ ActivityRecord[] outActivity, boolean restrictedBgActivity) {
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
- voiceInteractor);
+ voiceInteractor, restrictedBgActivity);
+
final int preferredWindowingMode = mLaunchParams.mWindowingMode;
computeLaunchingTaskFlags();
@@ -1658,7 +1687,7 @@
} else {
// This not being started from an existing activity, and not part of a new task...
// just put it in the top task, though these days this case should never happen.
- setTaskToCurrentTopOrCreateNewTask();
+ result = setTaskToCurrentTopOrCreateNewTask();
}
if (result != START_SUCCESS) {
return result;
@@ -1731,6 +1760,7 @@
mIntent = null;
mCallingUid = -1;
mOptions = null;
+ mRestrictedBgActivity = false;
mLaunchTaskBehind = false;
mLaunchFlags = 0;
@@ -1770,7 +1800,8 @@
private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
boolean doResume, int startFlags, ActivityRecord sourceRecord,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ boolean restrictedBgActivity) {
reset(false /* clearRequest */);
mStartActivity = r;
@@ -1780,6 +1811,7 @@
mSourceRecord = sourceRecord;
mVoiceSession = voiceSession;
mVoiceInteractor = voiceInteractor;
+ mRestrictedBgActivity = restrictedBgActivity;
mLaunchParams.reset();
@@ -1880,6 +1912,11 @@
}
mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
+
+ if (restrictedBgActivity) {
+ mAvoidMoveToFront = true;
+ mDoResume = false;
+ }
}
private void sendNewTaskResultRequestIfNeeded() {
@@ -2277,6 +2314,9 @@
// isLockTaskModeViolation fails below.
if (mReuseTask == null) {
+ if (mRestrictedBgActivity && handleBackgroundActivityAbort(mStartActivity)) {
+ return START_ABORTED;
+ }
final TaskRecord task = mTargetStack.createTaskRecord(
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId),
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
@@ -2289,6 +2329,11 @@
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+ " in new task " + mStartActivity.getTaskRecord());
} else {
+ if (mRestrictedBgActivity && !mReuseTask.containsAppUid(mCallingUid)) {
+ if (handleBackgroundActivityAbort(mStartActivity)) {
+ return START_ABORTED;
+ }
+ }
addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
}
@@ -2328,6 +2373,12 @@
final TaskRecord sourceTask = mSourceRecord.getTaskRecord();
final ActivityStack sourceStack = mSourceRecord.getActivityStack();
+ if (mRestrictedBgActivity && !sourceTask.containsAppUid(mCallingUid)) {
+ if (handleBackgroundActivityAbort(mStartActivity)) {
+ return START_ABORTED;
+ }
+ return START_ABORTED;
+ }
// We only want to allow changing stack in two cases:
// 1. If the target task is not the top one. Otherwise we would move the launching task to
// the other side, rather than show two side by side.
@@ -2489,20 +2540,33 @@
}
}
- private void setTaskToCurrentTopOrCreateNewTask() {
+ private int setTaskToCurrentTopOrCreateNewTask() {
mTargetStack = computeStackFocus(mStartActivity, false, mLaunchFlags, mOptions);
if (mDoResume) {
mTargetStack.moveToFront("addingToTopTask");
}
final ActivityRecord prev = mTargetStack.getTopActivity();
+ if (mRestrictedBgActivity && prev == null) {
+ if (handleBackgroundActivityAbort(mStartActivity)) {
+ return START_ABORTED;
+ }
+ return START_ABORTED;
+ }
final TaskRecord task = (prev != null)
? prev.getTaskRecord() : mTargetStack.createTaskRecord(
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mStartActivity.info,
mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions);
+ if (mRestrictedBgActivity && !task.containsAppUid(mCallingUid)) {
+ if (handleBackgroundActivityAbort(mStartActivity)) {
+ return START_ABORTED;
+ }
+ return START_ABORTED;
+ }
addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask");
mTargetStack.positionChildWindowContainerAtTop(task);
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+ " in new guessed " + mStartActivity.getTaskRecord());
+ return START_SUCCESS;
}
private void addOrReparentStartingActivity(TaskRecord parent, String reason) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 75e34fb..d4c4e6a 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -478,7 +478,7 @@
outReasons.put(windowingMode, APP_TRANSITION_WINDOWS_DRAWN);
} else {
outReasons.put(windowingMode,
- wtoken.startingData instanceof SplashScreenStartingData
+ wtoken.mStartingData instanceof SplashScreenStartingData
? APP_TRANSITION_SPLASH_SCREEN
: APP_TRANSITION_SNAPSHOT);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 81d6898..5be8e14 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -204,7 +204,7 @@
boolean removed;
// Information about an application starting window if displayed.
- StartingData startingData;
+ StartingData mStartingData;
WindowState startingWindow;
StartingSurface startingSurface;
boolean startingDisplayed;
@@ -386,8 +386,8 @@
// it from behind the starting window, so there is no need for it to also be doing its
// own stuff.
win.cancelAnimation();
- removeStartingWindow();
}
+ removeStartingWindow();
updateReportedVisibilityLocked();
}
@@ -875,7 +875,7 @@
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
+ this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
- if (startingData != null) {
+ if (mStartingData != null) {
removeStartingWindow();
}
@@ -1054,7 +1054,7 @@
// If this is the last window and we had requested a starting transition window,
// well there is no point now.
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingData");
- startingData = null;
+ mStartingData = null;
if (mHiddenSetFromTransferredStartingWindow) {
// We set the hidden state to false for the token from a transferred starting window.
// We now reset it back to true since the starting window was the last window in the
@@ -1487,13 +1487,13 @@
final long origId = Binder.clearCallingIdentity();
try {
// Transfer the starting window over to the new token.
- startingData = fromToken.startingData;
+ mStartingData = fromToken.mStartingData;
startingSurface = fromToken.startingSurface;
startingDisplayed = fromToken.startingDisplayed;
fromToken.startingDisplayed = false;
startingWindow = tStartingWindow;
reportedVisible = fromToken.reportedVisible;
- fromToken.startingData = null;
+ fromToken.mStartingData = null;
fromToken.startingSurface = null;
fromToken.startingWindow = null;
fromToken.startingMoved = true;
@@ -1539,13 +1539,13 @@
Binder.restoreCallingIdentity(origId);
}
return true;
- } else if (fromToken.startingData != null) {
+ } else if (fromToken.mStartingData != null) {
// The previous app was getting ready to show a
// starting window, but hasn't yet done so. Steal it!
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
"Moving pending starting from " + fromToken + " to " + this);
- startingData = fromToken.startingData;
- fromToken.startingData = null;
+ mStartingData = fromToken.mStartingData;
+ fromToken.mStartingData = null;
fromToken.startingMoved = true;
scheduleAddStartingWindow();
return true;
@@ -2043,7 +2043,7 @@
return false;
}
- if (startingData != null) {
+ if (mStartingData != null) {
return false;
}
@@ -2124,7 +2124,7 @@
}
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
- startingData = new SplashScreenStartingData(mWmService, pkg,
+ mStartingData = new SplashScreenStartingData(mWmService, pkg,
theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
getMergedOverrideConfiguration());
scheduleAddStartingWindow();
@@ -2138,7 +2138,7 @@
}
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
- startingData = new SnapshotStartingData(mWmService, snapshot);
+ mStartingData = new SnapshotStartingData(mWmService, snapshot);
scheduleAddStartingWindow();
return true;
}
@@ -2157,18 +2157,21 @@
@Override
public void run() {
+ // Can be accessed without holding the global lock
+ final StartingData startingData;
synchronized (mWmService.mGlobalLock) {
// There can only be one adding request, silly caller!
mWmService.mAnimationHandler.removeCallbacks(this);
- }
- if (startingData == null) {
- // Animation has been canceled... do nothing.
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG, "startingData was nulled out before handling"
- + " mAddStartingWindow: " + AppWindowToken.this);
+ if (mStartingData == null) {
+ // Animation has been canceled... do nothing.
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "startingData was nulled out before handling"
+ + " mAddStartingWindow: " + AppWindowToken.this);
+ }
+ return;
}
- return;
+ startingData = mStartingData;
}
if (DEBUG_STARTING_WINDOW) {
@@ -2186,20 +2189,21 @@
synchronized (mWmService.mGlobalLock) {
// If the window was successfully added, then
// we need to remove it.
- if (removed || startingData == null) {
+ if (removed || mStartingData == null) {
if (DEBUG_STARTING_WINDOW) {
Slog.v(TAG, "Aborted starting " + AppWindowToken.this
- + ": removed=" + removed + " startingData=" + startingData);
+ + ": removed=" + removed + " startingData=" + mStartingData);
}
startingWindow = null;
- startingData = null;
+ mStartingData = null;
abort = true;
} else {
startingSurface = surface;
}
if (DEBUG_STARTING_WINDOW && !abort) {
- Slog.v(TAG, "Added starting " + AppWindowToken.this + ": startingWindow="
- + startingWindow + " startingView=" + startingSurface);
+ Slog.v(TAG,
+ "Added starting " + AppWindowToken.this + ": startingWindow="
+ + startingWindow + " startingView=" + startingSurface);
}
}
if (abort) {
@@ -2246,21 +2250,21 @@
void removeStartingWindow() {
if (startingWindow == null) {
- if (startingData != null) {
+ if (mStartingData != null) {
// Starting window has not been added yet, but it is scheduled to be added.
// Go ahead and cancel the request.
if (DEBUG_STARTING_WINDOW) {
Slog.v(TAG_WM, "Clearing startingData for token=" + this);
}
- startingData = null;
+ mStartingData = null;
}
return;
}
final WindowManagerPolicy.StartingSurface surface;
- if (startingData != null) {
+ if (mStartingData != null) {
surface = startingSurface;
- startingData = null;
+ mStartingData = null;
startingSurface = null;
startingWindow = null;
startingDisplayed = false;
@@ -2553,12 +2557,17 @@
final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
if (a != null) {
+ // Only apply corner radius to animation if we're not in multi window mode.
+ // We don't want rounded corners when in pip or split screen.
+ final float windowCornerRadius = !inMultiWindowMode()
+ ? getDisplayContent().getWindowCornerRadius()
+ : 0;
adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
getDisplayContent().mAppTransition.canSkipFirstFrame(),
appStackClipMode,
true /* isAppAnimation */,
- getDisplayContent().getWindowCornerRadius()),
+ windowCornerRadius),
mWmService.mSurfaceAnimationRunner);
if (a.getZAdjustment() == Animation.ZORDER_TOP) {
mNeedsZBoost = true;
@@ -2995,8 +3004,8 @@
pw.print(prefix); pw.print("inPendingTransaction=");
pw.println(inPendingTransaction);
}
- if (startingData != null || removed || firstWindowDrawn || mIsExiting) {
- pw.print(prefix); pw.print("startingData="); pw.print(startingData);
+ if (mStartingData != null || removed || firstWindowDrawn || mIsExiting) {
+ pw.print(prefix); pw.print("startingData="); pw.print(mStartingData);
pw.print(" removed="); pw.print(removed);
pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
pw.print(" mIsExiting="); pw.println(mIsExiting);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6a5c84a..652f7ee 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -90,6 +90,9 @@
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.STACKS;
import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
+import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
+import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
+import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
@@ -2736,6 +2739,15 @@
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
+ for (int i = mOpeningApps.size() - 1; i >= 0; i--) {
+ mOpeningApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, OPENING_APPS);
+ }
+ for (int i = mClosingApps.size() - 1; i >= 0; i--) {
+ mClosingApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, CLOSING_APPS);
+ }
+ for (int i = mChangingApps.size() - 1; i >= 0; i--) {
+ mChangingApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, CHANGING_APPS);
+ }
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 26430fb..c23cdbb 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2720,11 +2720,18 @@
private void updateCurrentUserResources() {
final int userId = mService.mAmInternal.getCurrentUserId();
final Context uiContext = getSystemUiContext();
+
+ if (userId == UserHandle.USER_SYSTEM) {
+ // Skip the (expensive) recreation of resources for the system user below and just
+ // use the resources from the system ui context
+ mCurrentUserResources = uiContext.getResources();
+ return;
+ }
+
+ // For non-system users, ensure that the resources are loaded from the current
+ // user's package info (see ContextImpl.createDisplayContext)
final LoadedApk pi = ActivityThread.currentActivityThread().getPackageInfo(
uiContext.getPackageName(), null, 0, userId);
-
- // Create the resources from the current-user package info
- // (see ContextImpl.createDisplayContext)
mCurrentUserResources = ResourcesManager.getInstance().getResources(null,
pi.getResDir(),
null /* splitResDirs */,
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index e65a241..3ec461d 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -1174,8 +1174,9 @@
resumedOnDisplay |= result;
continue;
}
- if (topRunningActivity.isState(RESUMED)) {
- // Kick off any lingering app transitions form the MoveTaskToFront operation.
+ if (display.isTopStack(stack) && topRunningActivity.isState(RESUMED)) {
+ // Kick off any lingering app transitions form the MoveTaskToFront operation,
+ // but only consider the top task and stack on that display.
stack.executeAppTransition(targetOptions);
} else {
resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index d4d157fb..8505ec2 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1161,6 +1161,19 @@
return false;
}
+ /**
+ * Return true if any activities in this task belongs to input uid.
+ */
+ boolean containsAppUid(int uid) {
+ for (int i = mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.getUid() == uid) {
+ return true;
+ }
+ }
+ return false;
+ }
+
void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
if (mStack != null) {
for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8cbad2d..497c91a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4599,7 +4599,7 @@
anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
- getDisplayContent().getWindowCornerRadius()),
+ 0 /* windowCornerRadius */),
mWmService.mSurfaceAnimationRunner);
startAnimation(mPendingTransaction, adapter);
commitPendingTransaction();
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 4d37f1a..98c620c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -45,6 +45,13 @@
#include <string.h>
#include <utils/SystemClock.h>
+static jclass class_gnssMeasurementsEvent;
+static jclass class_gnssMeasurement;
+static jclass class_location;
+static jclass class_gnssNavigationMessage;
+static jclass class_gnssClock;
+static jclass class_gnssConfiguration_halInterfaceVersion;
+
static jobject mCallbacksObj = nullptr;
static jmethodID method_reportLocation;
@@ -95,6 +102,12 @@
static jmethodID method_correctionPlaneAzimDeg;
static jmethodID method_reportNfwNotification;
static jmethodID method_isInEmergencySession;
+static jmethodID method_gnssMeasurementsEventCtor;
+static jmethodID method_locationCtor;
+static jmethodID method_gnssNavigationMessageCtor;
+static jmethodID method_gnssClockCtor;
+static jmethodID method_gnssMeasurementCtor;
+static jmethodID method_halInterfaceVersionCtor;
/*
* Save a pointer to JavaVm to attach/detach threads executing
@@ -255,11 +268,11 @@
class JavaObject {
public:
- JavaObject(JNIEnv* env, const char* class_name);
- JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1);
- JavaObject(JNIEnv* env, const char* class_name, jobject object);
+ JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor);
+ JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char * sz_arg_1);
+ JavaObject(JNIEnv* env, jclass clazz, jobject object);
- virtual ~JavaObject();
+ virtual ~JavaObject() = default;
template<class T>
void callSetter(const char* method_name, T value);
@@ -273,25 +286,20 @@
jobject object_;
};
-JavaObject::JavaObject(JNIEnv* env, const char* class_name) : env_(env) {
- clazz_ = env_->FindClass(class_name);
- jmethodID ctor = env->GetMethodID(clazz_, "<init>", "()V");
- object_ = env_->NewObject(clazz_, ctor);
+JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor) : env_(env),
+ clazz_(clazz) {
+ object_ = env_->NewObject(clazz_, defaultCtor);
}
-JavaObject::JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1) : env_(env) {
- clazz_ = env_->FindClass(class_name);
- jmethodID ctor = env->GetMethodID(clazz_, "<init>", "(Ljava/lang/String;)V");
- object_ = env_->NewObject(clazz_, ctor, env->NewStringUTF(sz_arg_1));
+
+JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char * sz_arg_1)
+ : env_(env), clazz_(clazz) {
+ object_ = env_->NewObject(clazz_, stringCtor, env->NewStringUTF(sz_arg_1));
}
-JavaObject::JavaObject(JNIEnv* env, const char* class_name, jobject object)
- : env_(env), object_(object) {
- clazz_ = env_->FindClass(class_name);
-}
-JavaObject::~JavaObject() {
- env_->DeleteLocalRef(clazz_);
+JavaObject::JavaObject(JNIEnv* env, jclass clazz, jobject object)
+ : env_(env), clazz_(clazz), object_(object) {
}
template<class T>
@@ -358,11 +366,8 @@
}
static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
- jclass versionClass =
- env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion");
- jmethodID versionCtor = env->GetMethodID(versionClass, "<init>", "(II)V");
- jobject version = env->NewObject(versionClass, versionCtor, major, minor);
- env->DeleteLocalRef(versionClass);
+ jobject version = env->NewObject(class_gnssConfiguration_halInterfaceVersion,
+ method_halInterfaceVersionCtor, major, minor);
return version;
}
@@ -452,7 +457,7 @@
static jobject translateGnssLocation(JNIEnv* env,
const GnssLocation_V1_0& location) {
- JavaObject object(env, "android/location/Location", "gps");
+ JavaObject object(env, class_location, method_locationCtor, "gps");
uint16_t flags = static_cast<uint32_t>(location.gnssLocationFlags);
if (flags & GnssLocationFlags::HAS_LAT_LONG) {
@@ -488,8 +493,7 @@
static jobject translateGnssLocation(JNIEnv* env,
const GnssLocation_V2_0& location) {
- JavaObject object(env, "android/location/Location",
- translateGnssLocation(env, location.v1_0));
+ JavaObject object(env, class_location, translateGnssLocation(env, location.v1_0));
const uint16_t flags = static_cast<uint16_t>(location.elapsedRealtime.flags);
@@ -946,7 +950,7 @@
return Void();
}
- JavaObject object(env, "android/location/GnssNavigationMessage");
+ JavaObject object(env, class_gnssNavigationMessage, method_gnssNavigationMessageCtor);
SET(Type, static_cast<int32_t>(message.type));
SET(Svid, static_cast<int32_t>(message.svid));
SET(MessageId, static_cast<int32_t>(message.messageId));
@@ -1013,7 +1017,7 @@
void GnssMeasurementCallback::translateAndSetGnssData(const T& data) {
JNIEnv* env = getJniEnv();
- JavaObject gnssClockJavaObject(env, "android/location/GnssClock");
+ JavaObject gnssClockJavaObject(env, class_gnssClock, method_gnssClockCtor);
translateGnssClock(gnssClockJavaObject, data);
jobject clock = gnssClockJavaObject.get();
@@ -1175,41 +1179,30 @@
return nullptr;
}
- jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
jobjectArray gnssMeasurementArray = env->NewObjectArray(
count,
- gnssMeasurementClass,
+ class_gnssMeasurement,
nullptr /* initialElement */);
for (uint16_t i = 0; i < count; ++i) {
- JavaObject object(env, "android/location/GnssMeasurement");
+ JavaObject object(env, class_gnssMeasurement, method_gnssMeasurementCtor);
translateSingleGnssMeasurement(&(measurements[i]), object);
env->SetObjectArrayElement(gnssMeasurementArray, i, object.get());
}
- env->DeleteLocalRef(gnssMeasurementClass);
return gnssMeasurementArray;
}
void GnssMeasurementCallback::setMeasurementData(JNIEnv* env, jobject clock,
jobjectArray measurementArray) {
- jclass gnssMeasurementsEventClass =
- env->FindClass("android/location/GnssMeasurementsEvent");
- jmethodID gnssMeasurementsEventCtor =
- env->GetMethodID(
- gnssMeasurementsEventClass,
- "<init>",
- "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V");
-
- jobject gnssMeasurementsEvent = env->NewObject(gnssMeasurementsEventClass,
- gnssMeasurementsEventCtor,
+ jobject gnssMeasurementsEvent = env->NewObject(class_gnssMeasurementsEvent,
+ method_gnssMeasurementsEventCtor,
clock,
measurementArray);
env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData,
gnssMeasurementsEvent);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- env->DeleteLocalRef(gnssMeasurementsEventClass);
env->DeleteLocalRef(gnssMeasurementsEvent);
}
@@ -1464,8 +1457,7 @@
Return<void> GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(const hidl_vec<T>& locations) {
JNIEnv* env = getJniEnv();
- jobjectArray jLocations = env->NewObjectArray(locations.size(),
- env->FindClass("android/location/Location"), nullptr);
+ jobjectArray jLocations = env->NewObjectArray(locations.size(), class_location, nullptr);
for (uint16_t i = 0; i < locations.size(); ++i) {
jobject jLocation = translateGnssLocation(env, locations[i]);
@@ -1617,6 +1609,36 @@
method_correctionPlaneAltDeg = env->GetMethodID(refPlaneClass, "getAltitudeMeters", "()D");
method_correctionPlaneAzimDeg = env->GetMethodID(refPlaneClass, "getAzimuthDegrees", "()D");
+ jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent");
+ class_gnssMeasurementsEvent= (jclass) env->NewGlobalRef(gnssMeasurementsEventClass);
+ method_gnssMeasurementsEventCtor = env->GetMethodID(
+ class_gnssMeasurementsEvent,
+ "<init>",
+ "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V");
+
+ jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
+ class_gnssMeasurement = (jclass) env->NewGlobalRef(gnssMeasurementClass);
+ method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "<init>", "()V");
+
+ jclass locationClass = env->FindClass("android/location/Location");
+ class_location = (jclass) env->NewGlobalRef(locationClass);
+ method_locationCtor = env->GetMethodID(class_location, "<init>", "(Ljava/lang/String;)V");
+
+ jclass gnssNavigationMessageClass = env->FindClass("android/location/GnssNavigationMessage");
+ class_gnssNavigationMessage = (jclass) env->NewGlobalRef(gnssNavigationMessageClass);
+ method_gnssNavigationMessageCtor = env->GetMethodID(class_gnssNavigationMessage, "<init>", "()V");
+
+ jclass gnssClockClass = env->FindClass("android/location/GnssClock");
+ class_gnssClock = (jclass) env->NewGlobalRef(gnssClockClass);
+ method_gnssClockCtor = env->GetMethodID(class_gnssClock, "<init>", "()V");
+
+ jclass gnssConfiguration_halInterfaceVersionClass =
+ env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion");
+ class_gnssConfiguration_halInterfaceVersion =
+ (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass);
+ method_halInterfaceVersionCtor =
+ env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V");
+
/*
* Save a pointer to JVM.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 7600d4a..fe45411 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -505,14 +505,14 @@
private void assertHasStartingWindow(AppWindowToken atoken) {
assertNotNull(atoken.startingSurface);
- assertNotNull(atoken.startingData);
+ assertNotNull(atoken.mStartingData);
assertNotNull(atoken.startingWindow);
}
private void assertNoStartingWindow(AppWindowToken atoken) {
assertNull(atoken.startingSurface);
assertNull(atoken.startingWindow);
- assertNull(atoken.startingData);
+ assertNull(atoken.mStartingData);
atoken.forAllWindows(windowState -> {
assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
}, true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index b2084f8..0f7b35c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -61,6 +61,7 @@
import androidx.test.filters.MediumTest;
import com.android.internal.app.ResolverActivity;
+import com.android.server.wm.ActivityStack.ActivityState;
import org.junit.Before;
import org.junit.Test;
@@ -394,6 +395,52 @@
}
/**
+ * Verify that a lingering transition is being executed in case the activity to be resumed is
+ * already resumed
+ */
+ @Test
+ public void testResumeActivityLingeringTransition() {
+ // Create a stack at top.
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */));
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+ final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+ activity.setState(ActivityState.RESUMED, "test");
+
+ // Assume the stack is at the topmost position
+ assertTrue(targetStack.isTopStackOnDisplay());
+
+ // Use the stack as target to resume.
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
+
+ // Verify the lingering app transition is being executed because it's already resumed
+ verify(targetStack, times(1)).executeAppTransition(any());
+ }
+
+ @Test
+ public void testResumeActivityLingeringTransition_notExecuted() {
+ // Create a stack at bottom.
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */));
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+ final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+ activity.setState(ActivityState.RESUMED, "test");
+ display.positionChildAtBottom(targetStack);
+
+ // Assume the stack is at the topmost position
+ assertFalse(targetStack.isTopStackOnDisplay());
+ doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack();
+
+ // Use the stack as target to resume.
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
+
+ // Verify the lingering app transition is being executed because it's already resumed
+ verify(targetStack, never()).executeAppTransition(any());
+ }
+
+ /**
* Tests that home activities can be started on the displays that supports system decorations.
*/
@Test
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index e556b0a..1932871 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -29,6 +29,14 @@
}
android_test_helper_app {
+ name: "RollbackTestAppAv3",
+ manifest: "TestApp/Av3.xml",
+ sdk_version: "current",
+ srcs: ["TestApp/src/**/*.java"],
+ resource_dirs: ["TestApp/res_v3"],
+}
+
+android_test_helper_app {
name: "RollbackTestAppACrashingV2",
manifest: "TestApp/ACrashingV2.xml",
sdk_version: "current",
@@ -118,6 +126,7 @@
java_resources: [
":RollbackTestAppAv1",
":RollbackTestAppAv2",
+ ":RollbackTestAppAv3",
":RollbackTestAppACrashingV2",
":RollbackTestAppBv1",
":RollbackTestAppBv2",
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index f9304f2..13b5b9a 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -775,6 +775,42 @@
}
}
+ /**
+ * Test failure to enable rollback for multi-package installs.
+ * If any one of the packages fail to enable rollback, we shouldn't enable
+ * rollback for any package.
+ */
+ @Test
+ public void testMultiPackageEnableFail() throws Exception {
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.uninstall(TEST_APP_B);
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+
+ // We should fail to enable rollback here because TestApp B is not
+ // already installed.
+ RollbackTestUtils.installMultiPackage(true,
+ "RollbackTestAppAv2.apk",
+ "RollbackTestAppBv2.apk");
+
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+ assertNull(getUniqueRollbackInfoForPackage(
+ rm.getAvailableRollbacks(), TEST_APP_A));
+ assertNull(getUniqueRollbackInfoForPackage(
+ rm.getAvailableRollbacks(), TEST_APP_B));
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+ }
+
@Test
@Ignore("b/120200473")
/**
@@ -871,6 +907,51 @@
}
}
+ /**
+ * Test race between roll back and roll forward.
+ */
+ @Test
+ public void testRollForwardRace() throws Exception {
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
+ Manifest.permission.MANAGE_ROLLBACKS);
+
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+ RollbackTestUtils.uninstall(TEST_APP_A);
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+ RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+ rm.getAvailableRollbacks(), TEST_APP_A);
+ assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+
+ // Install a new version of package A, then immediately rollback
+ // the previous version. We expect the rollback to fail, because
+ // it is no longer available.
+ // There are a couple different ways this could fail depending on
+ // thread interleaving, so don't ignore flaky failures.
+ RollbackTestUtils.install("RollbackTestAppAv3.apk", false);
+ try {
+ RollbackTestUtils.rollback(rollback.getRollbackId());
+ // Note: Don't ignore flaky failures here.
+ fail("Expected rollback to fail, but it did not.");
+ } catch (AssertionError e) {
+ Log.i(TAG, "Note expected failure: ", e);
+ // Expected
+ }
+
+ // Note: Don't ignore flaky failures here.
+ assertEquals(3, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
+ }
+
// Helper function to test that the given rollback info is a rollback for
// the atomic set {A2, B2} -> {A1, B1}.
private void assertRollbackInfoForAandB(RollbackInfo rollback) {
diff --git a/tests/RollbackTest/TestApp/Av3.xml b/tests/RollbackTest/TestApp/Av3.xml
new file mode 100644
index 0000000..9725c9f7
--- /dev/null
+++ b/tests/RollbackTest/TestApp/Av3.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.rollback.testapp.A"
+ android:versionCode="3"
+ android:versionName="3.0" >
+
+
+ <uses-sdk android:minSdkVersion="19" />
+
+ <application android:label="Rollback Test App A v3">
+ <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+ android:exported="true" />
+ <activity android:name="com.android.tests.rollback.testapp.MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml
new file mode 100644
index 0000000..f2d8992
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <integer name="split_version">3</integer>
+</resources>
diff --git a/tests/RollbackTest/TestApp/res_v3/values/values.xml b/tests/RollbackTest/TestApp/res_v3/values/values.xml
new file mode 100644
index 0000000..968168a
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v3/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <integer name="app_version">3</integer>
+ <integer name="split_version">0</integer>
+</resources>
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index be227e7..ed41642 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1906,7 +1906,8 @@
if (this.meteredOverride != METERED_OVERRIDE_NONE) {
sbuf.append(" meteredOverride ").append(meteredOverride).append("\n");
}
- sbuf.append(" macRandomizationSetting ").append(macRandomizationSetting).append("\n");
+ sbuf.append(" macRandomizationSetting: ").append(macRandomizationSetting).append("\n");
+ sbuf.append(" mRandomizedMacAddress: ").append(mRandomizedMacAddress).append("\n");
sbuf.append(" KeyMgmt:");
for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
if (this.allowedKeyManagement.get(k)) {