Merge "Destroy Allocation with ScriptIntrinsicLut" into oc-dev
diff --git a/Android.mk b/Android.mk
index e58f306..8f99bc0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -322,7 +322,6 @@
core/java/android/service/chooser/IChooserTargetResult.aidl \
core/java/android/service/resolver/IResolverRankerService.aidl \
core/java/android/service/resolver/IResolverRankerResult.aidl \
- core/java/android/text/ITextClassificationService.aidl \
core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\
core/java/android/view/accessibility/IAccessibilityManager.aidl \
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index deb5b31..588a1bf 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1441,10 +1441,10 @@
System.err.println("Error: no size specified");
return showUsage();
}
- int len = size.length();
long multiplier = 1;
- if (len > 1) {
- char c = size.charAt(len-1);
+ int len = size.length();
+ char c = size.charAt(len - 1);
+ if (c < '0' || c > '9') {
if (c == 'K' || c == 'k') {
multiplier = 1024L;
} else if (c == 'M' || c == 'm') {
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index a5b37f3..8fd8043 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -370,15 +370,6 @@
"android.accounts.action.VISIBLE_ACCOUNTS_CHANGED";
/**
- * Key to set default visibility for applications targeting API level
- * {@link android.os.Build.VERSION_CODES#O} or above and don't have the same signature as
- * authenticator See {@link #getAccountVisibility}. If the value was not set by authenticator
- * {@link #VISIBILITY_USER_MANAGED_NOT_VISIBLE} is used.
- */
- public static final String PACKAGE_NAME_KEY_LEGACY_VISIBLE =
- "android:accounts:key_legacy_visible";
-
- /**
* Key to set visibility for applications which satisfy one of the following conditions:
* <ul>
* <li>Target API level below {@link android.os.Build.VERSION_CODES#O} and have
@@ -394,6 +385,14 @@
* See {@link #getAccountVisibility}. If the value was not set by authenticator
* {@link #VISIBILITY_USER_MANAGED_VISIBLE} is used.
*/
+ public static final String PACKAGE_NAME_KEY_LEGACY_VISIBLE =
+ "android:accounts:key_legacy_visible";
+
+ /**
+ * Key to set default visibility for applications which don't satisfy conditions in
+ * {@link PACKAGE_NAME_KEY_LEGACY_VISIBLE}. If the value was not set by authenticator
+ * {@link #VISIBILITY_USER_MANAGED_NOT_VISIBLE} is used.
+ */
public static final String PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE =
"android:accounts:key_legacy_not_visible";
@@ -608,14 +607,17 @@
}
/**
- * Returns the accounts visible to the specified package, in an environment where some apps are
+ * Returns the accounts visible to the specified package in an environment where some apps are
* not authorized to view all accounts. This method can only be called by system apps and
- * authenticators managing the type
+ * authenticators managing the type.
+ * Beginning API level {@link android.os.Build.VERSION_CODES#O} it also return accounts
+ * which user can make visible to the application (see {@link VISIBILITY_USER_MANAGED_VISIBLE}).
*
* @param type The type of accounts to return, null to retrieve all accounts
* @param packageName The package name of the app for which the accounts are to be returned
* @return An array of {@link Account}, one per matching account. Empty (never null) if no
- * accounts of the specified type have been added.
+ * accounts of the specified type can be accessed by the package.
+ *
*/
@NonNull
public Account[] getAccountsByTypeForPackage(String type, String packageName) {
@@ -644,7 +646,10 @@
*
* <p>
* Caller targeting API level {@link android.os.Build.VERSION_CODES#O} and above, will get list
- * of accounts made visible to it by user or AbstractAcccountAuthenticator and
+ * of accounts made visible to it by user
+ * (see {@link #newChooseAccountIntent(Account, List, String[], String,
+ * String, String[], Bundle)}) or AbstractAcccountAuthenticator
+ * using {@link setAccountVisibility}.
* {@link android.Manifest.permission#GET_ACCOUNTS} permission is not used.
*
* <p>
@@ -787,7 +792,10 @@
*
* <p>
* Caller targeting API level {@link android.os.Build.VERSION_CODES#O} and above, will get list
- * of accounts made visible to it by user or AbstractAcccountAuthenticator and
+ * of accounts made visible to it by user
+ * (see {@link #newChooseAccountIntent(Account, List, String[], String,
+ * String, String[], Bundle)}) or AbstractAcccountAuthenticator
+ * using {@link setAccountVisibility}.
* {@link android.Manifest.permission#GET_ACCOUNTS} permission is not used.
*
* <p>
@@ -869,7 +877,7 @@
}
/**
- * Adds an account directly to the AccountManager. Additionally it specifies Account visiblity
+ * Adds an account directly to the AccountManager. Additionally it specifies Account visibility
* for given list of packages.
* <p>
* Normally used by sign-up wizards associated with authenticators, not directly by
@@ -2663,8 +2671,8 @@
*
* <p>
* This method gets a list of the accounts matching specific type and feature set which are
- * visible to the caller or for which user can grant access (see {@link #getAccountsByType} for
- * details); if there is exactly one already visible account, it is used; if there are some
+ * visible to the caller (see {@link #getAccountsByType} for details);
+ * if there is exactly one already visible account, it is used; if there are some
* accounts for which user grant visibility, the user is prompted to pick one; if there are
* none, the user is prompted to add one. Finally, an auth token is acquired for the chosen
* account.
@@ -2735,6 +2743,9 @@
* <p>
* On success the activity returns a Bundle with the account name and type specified using
* keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}.
+ * Chosen account is marked as {@link #VISIBILITY_USER_MANAGED_VISIBLE} to the caller
+ * (see {@link setAccountVisibility}) and will be returned to it in consequent
+ * {@link #getAccountsByType}) calls.
* <p>
* The most common case is to call this with one account type, e.g.:
* <p>
@@ -2787,6 +2798,9 @@
* <p>
* On success the activity returns a Bundle with the account name and type specified using
* keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}.
+ * Chosen account is marked as {@link #VISIBILITY_USER_MANAGED_VISIBLE} to the caller
+ * (see {@link setAccountVisibility}) and will be returned to it in consequent
+ * {@link #getAccountsByType}) calls.
* <p>
* The most common case is to call this with one account type, e.g.:
* <p>
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4f6c0c9..37c287e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -25,6 +25,7 @@
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
import android.annotation.CallSuper;
@@ -3147,19 +3148,6 @@
}
/**
- * Called before {@link #onAttachedToWindow}.
- *
- * @hide
- */
- @CallSuper
- public void onBeforeAttachedToWindow() {
- if (mAutoFillResetNeeded) {
- getAutofillManager().onAttachedToWindow(
- getWindow().getDecorView().getRootView().getWindowToken());
- }
- }
-
- /**
* Called when the main window associated with the activity has been
* attached to the window manager.
* See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
@@ -7443,7 +7431,7 @@
final int offsetX = (anchorBounds != null)
? anchorBounds.left - actualAnchorBounds.left : 0;
int offsetY = (anchorBounds != null)
- ? anchorBounds.top - actualAnchorBounds.top : 0;
+ ? anchorBounds.bottom - actualAnchorBounds.bottom : 0;
final boolean wasShowing;
@@ -7471,45 +7459,62 @@
}
/** @hide */
+ @NonNull public View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds) {
+ final View[] views = new View[viewIds.length];
+ final ArrayList<ViewRootImpl> roots =
+ WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
+
+ for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+ final View rootView = roots.get(rootNum).getView();
+
+ if (rootView != null) {
+ for (int viewNum = 0; viewNum < viewIds.length; viewNum++) {
+ if (views[viewNum] == null) {
+ views[viewNum] = rootView.findViewByAccessibilityIdTraversal(
+ viewIds[viewNum]);
+ }
+ }
+ }
+ }
+
+ return views;
+ }
+
+ /** @hide */
@Override
- public boolean getViewVisibility(int viewId) {
- Window window = getWindow();
- if (window == null) {
- Log.i(TAG, "no window");
- return false;
- }
+ @NonNull public boolean[] getViewVisibility(@NonNull int[] viewIds) {
+ final boolean[] isVisible = new boolean[viewIds.length];
+ final View views[] = findViewsByAccessibilityIdTraversal(viewIds);
- View decorView = window.peekDecorView();
- if (decorView == null) {
- Log.i(TAG, "no decorView");
- return false;
- }
-
- View view = decorView.findViewByAccessibilityIdTraversal(viewId);
- if (view == null) {
- Log.i(TAG, "cannot find view");
- return false;
- }
-
- // Check if the view is visible by checking all parents
- while (view != null) {
- if (view == decorView) {
- break;
+ for (int i = 0; i < viewIds.length; i++) {
+ View view = views[i];
+ if (view == null) {
+ isVisible[i] = false;
+ continue;
}
- if (view.getVisibility() != View.VISIBLE) {
- Log.i(TAG, view + " is not visible");
- return false;
- }
+ isVisible[i] = true;
- if (view.getParent() instanceof View) {
- view = (View) view.getParent();
- } else {
- break;
+ // Check if the view is visible by checking all parents
+ while (true) {
+ if (view instanceof DecorView && view.getViewRootImpl() == view.getParent()) {
+ break;
+ }
+
+ if (view.getVisibility() != View.VISIBLE) {
+ isVisible[i] = false;
+ break;
+ }
+
+ if (view.getParent() instanceof View) {
+ view = (View) view.getParent();
+ } else {
+ break;
+ }
}
}
- return true;
+ return isVisible;
}
/** @hide */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index dd9db8a..928ef7e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4275,9 +4275,19 @@
View.mDebugViewAttributes = debugViewAttributes;
// request all activities to relaunch for the changes to take place
- for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
- requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false,
- false /* preserveWindow */);
+ requestRelaunchAllActivities();
+ }
+ }
+
+ private void requestRelaunchAllActivities() {
+ for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
+ final Activity activity = entry.getValue().activity;
+ if (!activity.mFinished) {
+ try {
+ ActivityManager.getService().requestActivityRelaunch(entry.getKey());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
}
@@ -5084,24 +5094,26 @@
// caused by other sources, such as overlays. That means we want to be as conservative
// about code changes as possible. Take the diff of the old ApplicationInfo and the new
// to see if anything needs to change.
+ LoadedApk apk;
+ LoadedApk resApk;
+ // Update all affected loaded packages with new package information
synchronized (mResourcesManager) {
- // Update all affected loaded packages with new package information
WeakReference<LoadedApk> ref = mPackages.get(ai.packageName);
- LoadedApk apk = ref != null ? ref.get() : null;
- if (apk != null) {
- final ArrayList<String> oldPaths = new ArrayList<>();
- LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths);
- apk.updateApplicationInfo(ai, oldPaths);
- }
-
- ref = mResourcePackages.get(ai.packageName);
apk = ref != null ? ref.get() : null;
- if (apk != null) {
- final ArrayList<String> oldPaths = new ArrayList<>();
- LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths);
- apk.updateApplicationInfo(ai, oldPaths);
- }
-
+ ref = mResourcePackages.get(ai.packageName);
+ resApk = ref != null ? ref.get() : null;
+ }
+ if (apk != null) {
+ final ArrayList<String> oldPaths = new ArrayList<>();
+ LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths);
+ apk.updateApplicationInfo(ai, oldPaths);
+ }
+ if (resApk != null) {
+ final ArrayList<String> oldPaths = new ArrayList<>();
+ LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths);
+ resApk.updateApplicationInfo(ai, oldPaths);
+ }
+ synchronized (mResourcesManager) {
// Update all affected Resources objects to use new ResourcesImpl
mResourcesManager.applyNewResourceDirsLocked(ai.sourceDir, ai.resourceDirs);
}
@@ -5116,14 +5128,7 @@
newConfig.assetsSeq = (mConfiguration != null ? mConfiguration.assetsSeq : 0) + 1;
handleConfigurationChanged(newConfig, null);
- // Schedule all activities to reload
- for (final Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
- final Activity activity = entry.getValue().activity;
- if (!activity.mFinished) {
- requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false,
- false);
- }
- }
+ requestRelaunchAllActivities();
}
static void freeTextLayoutCachesIfNeeded(int configDiff) {
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 63c0ef3..1b2543c 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -2497,7 +2497,6 @@
mBackStack = new ArrayList<BackStackRecord>();
}
mBackStack.add(state);
- reportBackStackChanged();
}
boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index aeccf56..68fce75 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -200,7 +200,7 @@
void setRequestedOrientation(in IBinder token, int requestedOrientation);
int getRequestedOrientation(in IBinder token);
void unbindFinished(in IBinder token, in Intent service, boolean doRebind);
- void setProcessForeground(in IBinder token, int pid, boolean isForeground);
+ void setProcessImportant(in IBinder token, int pid, boolean isForeground, String reason);
void setServiceForeground(in ComponentName className, in IBinder token,
int id, in Notification notification, int flags);
boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2e56bcf..0041879 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2675,6 +2675,7 @@
private int mActionBarColor = COLOR_INVALID;
private int mBackgroundColor = COLOR_INVALID;
private int mForegroundColor = COLOR_INVALID;
+ private int mBackgroundColorHint = COLOR_INVALID;
/**
* Constructs a new Builder with the defaults:
@@ -3839,6 +3840,13 @@
backgroundColor);
mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
backgroundColor);
+ if (backgroundColor != COLOR_DEFAULT
+ && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
+ mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
+ mPrimaryTextColor, backgroundColor, 4.5);
+ mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
+ mSecondaryTextColor, backgroundColor, 4.5);
+ }
} else {
double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);
double textLum = NotificationColorUtil.calculateLuminance(mForegroundColor);
@@ -4662,10 +4670,26 @@
if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
return mCachedContrastColor;
}
- final int contrasted = NotificationColorUtil.resolveContrastColor(mContext, mN.color);
+ int color;
+ int background = mBackgroundColorHint;
+ if (mBackgroundColorHint == COLOR_INVALID) {
+ background = mContext.getColor(
+ com.android.internal.R.color.notification_material_background_color);
+ }
+ if (mN.color == COLOR_DEFAULT) {
+ ensureColors();
+ color = mSecondaryTextColor;
+ } else {
+ color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
+ background);
+ }
+ if (Color.alpha(color) < 255) {
+ // alpha doesn't go well for color filters, so let's blend it manually
+ color = NotificationColorUtil.compositeColors(color, background);
+ }
mCachedContrastColorIsFor = mN.color;
- return mCachedContrastColor = contrasted;
+ return mCachedContrastColor = color;
}
int resolveAmbientColor() {
@@ -4882,7 +4906,8 @@
if (isColorized()) {
return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
} else {
- return COLOR_DEFAULT;
+ return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
+ : COLOR_DEFAULT;
}
}
@@ -4913,6 +4938,17 @@
mTextColorsAreForBackground = COLOR_INVALID;
ensureColors();
}
+
+ /**
+ * Sets the background color for this notification to be a different one then the default.
+ * This is mainly used to calculate contrast and won't necessarily be applied to the
+ * background.
+ *
+ * @hide
+ */
+ public void setBackgroundColorHint(int backgroundColor) {
+ mBackgroundColorHint = backgroundColor;
+ }
}
/**
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 040b330..8014eca 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -6,8 +6,6 @@
import android.os.RemoteException;
import android.service.vr.IVrManager;
-import java.io.FileDescriptor;
-
/**
* Used to control aspects of a devices Virtual Reality (VR) capabilities.
* <p>
@@ -63,32 +61,4 @@
e.rethrowFromSystemServer();
}
}
-
- /**
- * Initiate connection for system controller data.
- *
- * @param fd Controller data file descriptor.
- *
- * {@hide}
- */
- public void connectController(FileDescriptor fd) {
- try {
- mService.connectController(fd);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Sever connection for system controller data.
- *
- * {@hide}
- */
- public void disconnectController() {
- try {
- mService.disconnectController();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
}
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index 28704e6..995d98a 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -71,11 +71,12 @@
public static final int TYPE_INSTALL_WINDOWED = 2;
/**
- * Incoming system updates (except for security updates) will be blocked for 30 days, after
- * which the policy will no longer be effective and the system will revert back to its normal
- * behavior as if no policy were set.
- * <b>Note:</b> security updates (e.g. monthly security patches) will <i>not</i> be affected by
- * this policy.
+ * Incoming system updates (except for security updates) will be blocked for a maximum of 30
+ * days, after which the policy will no longer be effective and the system will revert back to
+ * its normal behavior as if no policy were set.
+ *
+ * <p><b>Note:</b> security updates (e.g. monthly security patches) may <i>not</i> be affected
+ * by this policy, depending on the policy set by the device manufacturer and carrier.
*
* <p>After this policy expires, resetting it to any policy other than
* {@link #TYPE_INSTALL_AUTOMATIC} will produce no effect, as the 30-day maximum delay has
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 7261dfa..1994206 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -18,6 +18,7 @@
import android.os.PooledStringWriter;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.service.autofill.FillContext;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -1103,6 +1104,9 @@
* Returns the transformation that has been applied to this view, such as a translation
* or scaling. The returned Matrix object is owned by ViewNode; do not modify it.
* Returns null if there is no transformation applied to the view.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public Matrix getTransformation() {
return mMatrix;
@@ -1112,6 +1116,9 @@
* Returns the visual elevation of the view, used for shadowing and other visual
* characterstics, as set by {@link ViewStructure#setElevation
* ViewStructure.setElevation(float)}.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public float getElevation() {
return mElevation;
@@ -1121,6 +1128,9 @@
* Returns the alpha transformation of the view, used to reduce the overall opacity
* of the view's contents, as set by {@link ViewStructure#setAlpha
* ViewStructure.setAlpha(float)}.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public float getAlpha() {
return mAlpha;
@@ -1289,6 +1299,9 @@
/**
* If {@link #getText()} is non-null, this is where the current selection starts.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int getTextSelectionStart() {
return mText != null ? mText.mTextSelectionStart : -1;
@@ -1298,6 +1311,9 @@
* If {@link #getText()} is non-null, this is where the current selection starts.
* If there is no selection, returns the same value as {@link #getTextSelectionStart()},
* indicating the cursor position.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int getTextSelectionEnd() {
return mText != null ? mText.mTextSelectionEnd : -1;
@@ -1319,6 +1335,9 @@
* If there is no text background color, {@link #TEXT_COLOR_UNDEFINED} is returned.
* Note that the text may also contain style spans that modify the color of specific
* parts of the text.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int getTextBackgroundColor() {
return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
@@ -1329,6 +1348,9 @@
* with it.
* Note that the text may also contain style spans that modify the size of specific
* parts of the text.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public float getTextSize() {
return mText != null ? mText.mTextSize : 0;
@@ -1341,6 +1363,9 @@
* {@link #TEXT_STYLE_UNDERLINE}.
* Note that the text may also contain style spans that modify the style of specific
* parts of the text.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int getTextStyle() {
return mText != null ? mText.mTextStyle : 0;
@@ -1351,6 +1376,9 @@
* in the array is a formatted line of text, and the value it contains is the offset
* into the text string where that line starts. May return null if there is no line
* information.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int[] getTextLineCharOffsets() {
return mText != null ? mText.mLineCharOffsets : null;
@@ -1361,6 +1389,9 @@
* in the array is a formatted line of text, and the value it contains is the baseline
* where that text appears in the view. May return null if there is no line
* information.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int[] getTextLineBaselines() {
return mText != null ? mText.mLineBaselines : null;
diff --git a/core/java/android/app/usage/ExternalStorageStats.java b/core/java/android/app/usage/ExternalStorageStats.java
index 83ac779..d7e570f 100644
--- a/core/java/android/app/usage/ExternalStorageStats.java
+++ b/core/java/android/app/usage/ExternalStorageStats.java
@@ -37,30 +37,46 @@
/**
* Return the total bytes used by all files in the shared/external storage
* hosted on this volume.
+ * <p>
+ * This value only includes data which is isolated for each user on a
+ * multiuser device. Any OBB data shared between users is not accounted in
+ * this value.
*/
public @BytesLong long getTotalBytes() {
return totalBytes;
}
/**
- * Return the total bytes used by audio files in the shared/external storage
- * hosted on this volume.
+ * Return the total bytes used by all audio files in the shared/external
+ * storage hosted on this volume.
+ * <p>
+ * This value only includes data which is isolated for each user on a
+ * multiuser device. This value does not include any app files which are all
+ * accounted under {@link #getAppBytes()}.
*/
public @BytesLong long getAudioBytes() {
return audioBytes;
}
/**
- * Return the total bytes used by video files in the shared/external storage
- * hosted on this volume.
+ * Return the total bytes used by all video files in the shared/external
+ * storage hosted on this volume.
+ * <p>
+ * This value only includes data which is isolated for each user on a
+ * multiuser device. This value does not include any app files which are all
+ * accounted under {@link #getAppBytes()}.
*/
public @BytesLong long getVideoBytes() {
return videoBytes;
}
/**
- * Return the total bytes used by image files in the shared/external storage
- * hosted on this volume.
+ * Return the total bytes used by all image files in the shared/external
+ * storage hosted on this volume.
+ * <p>
+ * This value only includes data which is isolated for each user on a
+ * multiuser device. This value does not include any app files which are all
+ * accounted under {@link #getAppBytes()}.
*/
public @BytesLong long getImageBytes() {
return imageBytes;
@@ -72,6 +88,9 @@
* <p>
* This data is already accounted against individual apps as returned
* through {@link StorageStats}.
+ * <p>
+ * This value only includes data which is isolated for each user on a
+ * multiuser device.
*/
public @BytesLong long getAppBytes() {
return appBytes;
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index e2d4f5b..dc5c7b6 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -43,10 +43,10 @@
interface IBluetoothGatt {
List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
- void registerScanner(in IScannerCallback callback);
+ void registerScanner(in IScannerCallback callback, in WorkSource workSource);
void unregisterScanner(in int scannerId);
void startScan(in int scannerId, in ScanSettings settings, in List<ScanFilter> filters,
- in WorkSource workSource, in List scanStorages, in String callingPackage);
+ in List scanStorages, in String callingPackage);
void startScanForIntent(in PendingIntent intent, in ScanSettings settings, in List<ScanFilter> filters,
in String callingPackage);
void stopScanForIntent(in PendingIntent intent, in String callingPackage);
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 5246513..f3f0ae5 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -360,7 +360,7 @@
// Scan stopped.
if (mScannerId == -1) return;
try {
- mBluetoothGatt.registerScanner(this);
+ mBluetoothGatt.registerScanner(this, mWorkSource);
wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS);
} catch (InterruptedException | RemoteException e) {
Log.e(TAG, "application registeration exception", e);
@@ -424,7 +424,7 @@
} else {
mScannerId = scannerId;
mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
- mWorkSource, mResultStorages,
+ mResultStorages,
ActivityThread.currentOpPackageName());
}
} catch (RemoteException e) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bf7af20..10594af 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2891,6 +2891,7 @@
CLIPBOARD_SERVICE,
INPUT_METHOD_SERVICE,
TEXT_SERVICES_MANAGER_SERVICE,
+ TEXT_CLASSIFICATION_SERVICE,
APPWIDGET_SERVICE,
//@hide: VOICE_INTERACTION_MANAGER_SERVICE,
//@hide: BACKUP_SERVICE,
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 5264cd7..e127ca3 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -952,4 +952,12 @@
public Handler getMainThreadHandler() {
return mBase.getMainThreadHandler();
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public int getNextAccessibilityId() {
+ return mBase.getNextAccessibilityId();
+ }
}
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 603192a..dcaf66e 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -47,7 +47,7 @@
private final int mVersionCode;
public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
- @Nullable List<InstantAppIntentFilter> filters, int versionConde) {
+ @Nullable List<InstantAppIntentFilter> filters, int versionCode) {
// validate arguments
if ((packageName == null && (filters != null && filters.size() != 0))
|| (packageName != null && (filters == null || filters.size() == 0))) {
@@ -61,7 +61,7 @@
mFilters = null;
}
mPackageName = packageName;
- mVersionCode = versionConde;
+ mVersionCode = versionCode;
}
public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 2b76ae2..d0c6397 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -23,7 +23,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -96,6 +95,9 @@
public static final int FLAG_ADAPTIVE_BITMAP = 1 << 9;
/** @hide */
+ public static final int FLAG_RETURNED_BY_SERVICE = 1 << 10;
+
+ /** @hide */
@IntDef(flag = true,
value = {
FLAG_DYNAMIC,
@@ -108,6 +110,7 @@
FLAG_STRINGS_RESOLVED,
FLAG_IMMUTABLE,
FLAG_ADAPTIVE_BITMAP,
+ FLAG_RETURNED_BY_SERVICE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ShortcutFlags {}
@@ -1343,6 +1346,16 @@
return (mFlags & flags) == flags;
}
+ /** @hide */
+ public boolean isReturnedByServer() {
+ return hasFlags(FLAG_RETURNED_BY_SERVICE);
+ }
+
+ /** @hide */
+ public void setReturnedByServer() {
+ addFlags(FLAG_RETURNED_BY_SERVICE);
+ }
+
/** Return whether a shortcut is dynamic. */
public boolean isDynamic() {
return hasFlags(FLAG_DYNAMIC);
@@ -1450,7 +1463,7 @@
/**
* Return whether a shortcut's icon is adaptive bitmap following design guideline
- * defined in {@link AdaptiveIconDrawable}.
+ * defined in {@link android.graphics.drawable.AdaptiveIconDrawable}.
*
* @hide internal/unit tests only
*/
@@ -1776,6 +1789,9 @@
if (hasStringResourcesResolved()) {
sb.append("Sr");
}
+ if (isReturnedByServer()) {
+ sb.append("V");
+ }
sb.append("]");
sb.append(", packageName=");
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index a7c09b5..e3e0cc5 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -618,6 +618,10 @@
/**
* Return all dynamic shortcuts from the caller app.
*
+ * <p>This API is intended to be used for examining what shortcuts are currently published.
+ * Re-publishing returned {@link ShortcutInfo}s via APIs such as
+ * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
+ *
* @throws IllegalStateException when the user is locked.
*/
@NonNull
@@ -633,6 +637,10 @@
/**
* Return all static (manifest) shortcuts from the caller app.
*
+ * <p>This API is intended to be used for examining what shortcuts are currently published.
+ * Re-publishing returned {@link ShortcutInfo}s via APIs such as
+ * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
+ *
* @throws IllegalStateException when the user is locked.
*/
@NonNull
@@ -697,6 +705,10 @@
/**
* Return all pinned shortcuts from the caller app.
*
+ * <p>This API is intended to be used for examining what shortcuts are currently published.
+ * Re-publishing returned {@link ShortcutInfo}s via APIs such as
+ * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
+ *
* @throws IllegalStateException when the user is locked.
*/
@NonNull
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index b21ccf1..042eb87 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -18,6 +18,7 @@
import com.android.internal.R;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
@@ -36,8 +37,6 @@
*/
public class FontResourcesParser {
private static final String TAG = "FontResourcesParser";
- private static final int NORMAL_WEIGHT = 400;
- private static final int ITALIC = 1;
// A class represents single entry of font-family in xml file.
public interface FamilyResourceEntry {}
@@ -78,10 +77,10 @@
public static final class FontFileResourceEntry {
private final @NonNull String mFileName;
private int mWeight;
- private boolean mItalic;
+ private int mItalic;
private int mResourceId;
- public FontFileResourceEntry(@NonNull String fileName, int weight, boolean italic) {
+ public FontFileResourceEntry(@NonNull String fileName, int weight, int italic) {
mFileName = fileName;
mWeight = weight;
mItalic = italic;
@@ -95,7 +94,7 @@
return mWeight;
}
- public boolean isItalic() {
+ public int getItalic() {
return mItalic;
}
}
@@ -200,8 +199,10 @@
throws XmlPullParserException, IOException {
AttributeSet attrs = Xml.asAttributeSet(parser);
TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamilyFont);
- int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight, NORMAL_WEIGHT);
- boolean isItalic = ITALIC == array.getInt(R.styleable.FontFamilyFont_fontStyle, 0);
+ int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight,
+ Typeface.RESOLVE_BY_FONT_TABLE);
+ int italic = array.getInt(R.styleable.FontFamilyFont_fontStyle,
+ Typeface.RESOLVE_BY_FONT_TABLE);
String filename = array.getString(R.styleable.FontFamilyFont_font);
array.recycle();
while (parser.next() != XmlPullParser.END_TAG) {
@@ -210,7 +211,7 @@
if (filename == null) {
return null;
}
- return new FontFileResourceEntry(filename, weight, isItalic);
+ return new FontFileResourceEntry(filename, weight, italic);
}
private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 211d54d..63eedf5 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -107,7 +107,8 @@
/**
* Create a request suitable for zero shutter lag still capture. This means
* means maximizing image quality without compromising preview frame rate.
- * AE/AWB/AF should be on auto mode.
+ * AE/AWB/AF should be on auto mode. This is intended for application-operated ZSL. For
+ * device-operated ZSL, use {@link CaptureRequest#CONTROL_ENABLE_ZSL} if available.
* This template is guaranteed to be supported on camera devices that support the
* {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING PRIVATE_REPROCESSING}
* capability or the
@@ -115,6 +116,7 @@
* capability.
*
* @see #createCaptureRequest
+ * @see CaptureRequest#CONTROL_ENABLE_ZSL
*/
public static final int TEMPLATE_ZERO_SHUTTER_LAG = 5;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 279d73d..c41fc02 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1669,6 +1669,7 @@
* <code>false</code> if present.</p>
* <p>For applications targeting SDK versions older than O, the value of enableZsl in all
* capture templates is always <code>false</code> if present.</p>
+ * <p>For application-operated ZSL, use CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG template.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index aedfc4b..6d80c20 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2174,6 +2174,7 @@
* <code>false</code> if present.</p>
* <p>For applications targeting SDK versions older than O, the value of enableZsl in all
* capture templates is always <code>false</code> if present.</p>
+ * <p>For application-operated ZSL, use CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG template.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 7947620..d61fb97 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -42,8 +42,8 @@
import javax.crypto.Mac;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.USE_FINGERPRINT;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.USE_FINGERPRINT;
/**
* A class that coordinates access to the fingerprint hardware.
@@ -910,6 +910,7 @@
} else if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
getErrorString(errMsgId, vendorCode));
+ mAuthenticationCallback = null;
} else if (mRemovalCallback != null) {
mRemovalCallback.onRemovalError(mRemovalFingerprint, clientErrMsgId,
getErrorString(errMsgId, vendorCode));
@@ -930,12 +931,14 @@
final AuthenticationResult result =
new AuthenticationResult(mCryptoObject, fp, userId);
mAuthenticationCallback.onAuthenticationSucceeded(result);
+ mAuthenticationCallback = null;
}
}
private void sendAuthenticatedFailed() {
if (mAuthenticationCallback != null) {
- mAuthenticationCallback.onAuthenticationFailed();
+ mAuthenticationCallback.onAuthenticationFailed();
+ mAuthenticationCallback = null;
}
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 235f24c..b5fd116 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -183,8 +183,10 @@
* - Wakelock data (wl) gets current and max times.
* New in version 20:
* - Background timers and counters for: Sensor, BluetoothScan, WifiScan, Jobs, Syncs.
+ * New in version 21:
+ * - Actual (not just apportioned) Wakelock time is also recorded.
*/
- static final String CHECKIN_VERSION = "20";
+ static final String CHECKIN_VERSION = "21";
/**
* Old version, we hit 9 and ran out of room, need to remove.
@@ -194,7 +196,7 @@
private static final long BYTES_PER_KB = 1024;
private static final long BYTES_PER_MB = 1048576; // 1024^2
private static final long BYTES_PER_GB = 1073741824; //1024^3
-
+
private static final String VERSION_DATA = "vers";
private static final String UID_DATA = "uid";
private static final String WAKEUP_ALARM_DATA = "wua";
@@ -205,6 +207,12 @@
private static final String VIBRATOR_DATA = "vib";
private static final String FOREGROUND_DATA = "fg";
private static final String STATE_TIME_DATA = "st";
+ // wl line is:
+ // BATTERY_STATS_CHECKIN_VERSION, uid, which, "wl", name,
+ // full totalTime, 'f', count, current duration, max duration, total duration,
+ // partial totalTime, 'p', count, current duration, max duration, total duration,
+ // window totalTime, 'w', count, current duration, max duration, total duration
+ // [Currently, full and window wakelocks have durations current = max = total = -1]
private static final String WAKELOCK_DATA = "wl";
private static final String SYNC_DATA = "sy";
private static final String JOB_DATA = "jb";
@@ -513,6 +521,7 @@
public abstract Timer getForegroundActivityTimer();
public abstract Timer getBluetoothScanTimer();
public abstract Timer getBluetoothScanBackgroundTimer();
+ public abstract Counter getBluetoothScanResultCounter();
// Note: the following times are disjoint. They can be added together to find the
// total time a uid has had any processes running at all.
@@ -2659,6 +2668,12 @@
sb.append(" max=");
sb.append(maxDurationMs);
}
+ // Put actual time if it is available and different from totalTimeMillis.
+ final long totalDurMs = timer.getTotalDurationMsLocked(elapsedRealtimeUs/1000);
+ if (totalDurMs > totalTimeMillis) {
+ sb.append(" actual=");
+ sb.append(totalDurMs);
+ }
if (timer.isRunningLocked()) {
final long currentMs = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000);
if (currentMs >= 0) {
@@ -2742,13 +2757,15 @@
long elapsedRealtimeUs, String name, int which, String linePrefix) {
long totalTimeMicros = 0;
int count = 0;
- long max = -1;
- long current = -1;
+ long max = 0;
+ long current = 0;
+ long totalDuration = 0;
if (timer != null) {
totalTimeMicros = timer.getTotalTimeLocked(elapsedRealtimeUs, which);
- count = timer.getCountLocked(which);
+ count = timer.getCountLocked(which);
current = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000);
max = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000);
+ totalDuration = timer.getTotalDurationMsLocked(elapsedRealtimeUs/1000);
}
sb.append(linePrefix);
sb.append((totalTimeMicros + 500) / 1000); // microseconds to milliseconds with rounding
@@ -2759,9 +2776,16 @@
sb.append(current);
sb.append(',');
sb.append(max);
+ // Partial, full, and window wakelocks are pooled, so totalDuration is meaningful (albeit
+ // not always tracked). Kernel wakelocks (which have name == null) have no notion of
+ // totalDuration independent of totalTimeMicros (since they are not pooled).
+ if (name != null) {
+ sb.append(',');
+ sb.append(totalDuration);
+ }
return ",";
}
-
+
private static final void dumpLineHeader(PrintWriter pw, int uid, String category,
String type) {
pw.print(BATTERY_STATS_CHECKIN_VERSION);
@@ -3347,8 +3371,10 @@
final long actualTime = bleTimer.getTotalDurationMsLocked(rawRealtimeMs);
final long actualTimeBg = bleTimerBg != null ?
bleTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+ final int resultCount = u.getBluetoothScanResultCounter() != null ?
+ u.getBluetoothScanResultCounter().getCountLocked(which) : 0;
dumpLine(pw, uid, category, BLUETOOTH_MISC_DATA, totalTime, count,
- countBg, actualTime, actualTimeBg);
+ countBg, actualTime, actualTimeBg, resultCount);
}
}
@@ -4500,6 +4526,8 @@
final long actualTimeMs = bleTimer.getTotalDurationMsLocked(rawRealtimeMs);
final long actualTimeMsBg = bleTimerBg != null ?
bleTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+ final int resultCount = u.getBluetoothScanResultCounter() != null ?
+ u.getBluetoothScanResultCounter().getCountLocked(which) : 0;
sb.setLength(0);
sb.append(prefix);
@@ -4524,6 +4552,8 @@
sb.append(countBg);
sb.append(" times)");
}
+ sb.append("; Results count ");
+ sb.append(resultCount);
pw.println(sb.toString());
uidActivity = true;
}
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index eceaa31..6aa601a 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -189,10 +189,11 @@
if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
throw new IllegalArgumentException(
"amplitude must either be DEFAULT_AMPLITUDE, " +
- "or between 1 and 255 inclusive");
+ "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
}
if (mTiming <= 0) {
- throw new IllegalArgumentException("timing must be positive");
+ throw new IllegalArgumentException(
+ "timing must be positive (timing=" + mTiming + ")");
}
}
@@ -274,24 +275,31 @@
public void validate() {
if (mTimings.length != mAmplitudes.length) {
throw new IllegalArgumentException(
- "timing and amplitude arrays must be of equal length");
+ "timing and amplitude arrays must be of equal length" +
+ " (timings.length=" + mTimings.length +
+ ", amplitudes.length=" + mAmplitudes.length + ")");
}
if (!hasNonZeroEntry(mTimings)) {
- throw new IllegalArgumentException("at least one timing must be non-zero");
+ throw new IllegalArgumentException("at least one timing must be non-zero" +
+ " (timings=" + Arrays.toString(mTimings) + ")");
}
for (long timing : mTimings) {
if (timing < 0) {
- throw new IllegalArgumentException("timings must all be >= 0");
+ throw new IllegalArgumentException("timings must all be >= 0" +
+ " (timings=" + Arrays.toString(mTimings) + ")");
}
}
for (int amplitude : mAmplitudes) {
if (amplitude < -1 || amplitude > 255) {
throw new IllegalArgumentException(
- "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255");
+ "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255" +
+ " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
}
}
if (mRepeat < -1 || mRepeat >= mTimings.length) {
- throw new IllegalArgumentException("repeat index must be >= -1");
+ throw new IllegalArgumentException(
+ "repeat index must be within the bounds of the timings array" +
+ " (timings.length=" + mTimings.length + ", index=" + mRepeat +")");
}
}
@@ -375,7 +383,8 @@
@Override
public void validate() {
if (mEffectId != EFFECT_CLICK) {
- throw new IllegalArgumentException("Unknown prebaked effect type");
+ throw new IllegalArgumentException(
+ "Unknown prebaked effect type (value=" + mEffectId + ")");
}
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 1e55c78..2f0eeca 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -157,6 +157,8 @@
// This call needs to continue throwing ArrayIndexOutOfBoundsException but ignore all other
// exceptions for compatibility purposes
if (repeat < -1 || repeat >= pattern.length) {
+ Log.e(TAG, "vibrate called with repeat index out of bounds" +
+ " (pattern.length=" + pattern.length + ", index=" + repeat + ")");
throw new ArrayIndexOutOfBoundsException();
}
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 8302ece..65b33e5 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -16,14 +16,17 @@
package android.os;
-import java.util.ArrayList;
+import java.util.Map;
import android.util.Log;
-/** @hide */
+/**
+ * Java API for libvintf.
+ * @hide
+ */
public class VintfObject {
- private static final String LOG_TAG = "VintfObject";
+ /// ---------- OTA
/**
* Slurps all device information (both manifests and both matrices)
@@ -45,4 +48,26 @@
*/
public static native int verify(String[] packageInfo);
+ /// ---------- CTS Device Info
+
+ /**
+ * @return a list of HAL names and versions that is supported by this
+ * device as stated in device and framework manifests. For example,
+ * ["android.hidl.manager@1.0", "android.hardware.camera.device@1.0",
+ * "android.hardware.camera.device@3.2"]. There are no duplicates.
+ */
+ public static native String[] getHalNamesAndVersions();
+
+ /**
+ * @return the BOARD_SEPOLICY_VERS build flag available in device manifest.
+ */
+ public static native String getSepolicyVersion();
+
+ /**
+ * @return a list of VNDK snapshots supported by the framework, as
+ * specified in framework manifest. For example,
+ * [("25.0.5", ["libjpeg.so", "libbase.so"]),
+ * ("25.1.3", ["libjpeg.so", "libbase.so"])]
+ */
+ public static native Map<String, String[]> getVndkSnapshots();
}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 88d17ef..5fd9458 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -15,6 +15,7 @@
*/
package android.service.autofill;
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
@@ -156,6 +157,7 @@
*
* <strong>NOTE: </strong>if overridden, it must call {@code super.onCreate()}.
*/
+ @CallSuper
@Override
public void onCreate() {
super.onCreate();
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 277c622..9487760 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -174,6 +174,7 @@
SAVE_DATA_TYPE_PASSWORD,
SAVE_DATA_TYPE_ADDRESS,
SAVE_DATA_TYPE_CREDIT_CARD,
+ SAVE_DATA_TYPE_USERNAME,
SAVE_DATA_TYPE_EMAIL_ADDRESS})
@Retention(RetentionPolicy.SOURCE)
@interface SaveDataType{}
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index fc8afe9..9b37a65 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -86,17 +86,5 @@
* currently, else return the display id of the virtual display
*/
int getVr2dDisplayId();
-
- /**
- * Initiate connection for system controller data.
- *
- * @param fd Controller data file descriptor.
- */
- void connectController(in FileDescriptor fd);
-
- /**
- * Sever connection for system controller data.
- */
- void disconnectController();
}
diff --git a/core/java/android/text/ITextClassificationService.aidl b/core/java/android/text/ITextClassificationService.aidl
deleted file mode 100644
index a73dbf0..0000000
--- a/core/java/android/text/ITextClassificationService.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import android.os.ParcelFileDescriptor;
-
-/**
- * Interface to the text classification service, which grants access to the text classification
- * LSTM model file.
- * {@hide}
- */
-interface ITextClassificationService {
-
- /**
- * Request a file descriptor with read-only access to the LSTM model file.
- * This file descriptor should be closed after the client is done with it.
- */
- ParcelFileDescriptor getModelFileFd();
-}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 91f43d6..585f882 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1520,6 +1520,18 @@
/**
* Returns a CharSequence concatenating the specified CharSequences,
* retaining their spans if any.
+ *
+ * If there are no parameters, an empty string will be returned.
+ *
+ * If the number of parameters is exactly one, that parameter is returned as output, even if it
+ * is null.
+ *
+ * If the number of parameters is at least two, any null CharSequence among the parameters is
+ * treated as if it was the string <code>"null"</code>.
+ *
+ * If there are paragraph spans in the source CharSequences that satisfy paragraph boundary
+ * requirements in the sources but would no longer satisfy them in the concatenated
+ * CharSequence, they may get extended in the resulting CharSequence or not retained.
*/
public static CharSequence concat(CharSequence... text) {
if (text.length == 0) {
@@ -1531,35 +1543,29 @@
}
boolean spanned = false;
- for (int i = 0; i < text.length; i++) {
- if (text[i] instanceof Spanned) {
+ for (CharSequence piece : text) {
+ if (piece instanceof Spanned) {
spanned = true;
break;
}
}
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < text.length; i++) {
- sb.append(text[i]);
- }
-
- if (!spanned) {
+ if (spanned) {
+ final SpannableStringBuilder ssb = new SpannableStringBuilder();
+ for (CharSequence piece : text) {
+ // If a piece is null, we append the string "null" for compatibility with the
+ // behavior of StringBuilder and the behavior of the concat() method in earlier
+ // versions of Android.
+ ssb.append(piece == null ? "null" : piece);
+ }
+ return new SpannedString(ssb);
+ } else {
+ final StringBuilder sb = new StringBuilder();
+ for (CharSequence piece : text) {
+ sb.append(piece);
+ }
return sb.toString();
}
-
- SpannableString ss = new SpannableString(sb);
- int off = 0;
- for (int i = 0; i < text.length; i++) {
- int len = text[i].length();
-
- if (text[i] instanceof Spanned) {
- copySpansFrom((Spanned) text[i], 0, len, Object.class, ss, off);
- }
-
- off += len;
- }
-
- return new SpannedString(ss);
}
/**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f539752..2ade9b5 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -678,6 +678,7 @@
mIsCreating = false;
if (mSurfaceControl != null && !mSurfaceCreated) {
mSurfaceControl.destroy();
+ mSurface.release();
mSurfaceControl = null;
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 39f1170..6ee6d63 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6844,7 +6844,7 @@
if (isAutofillable() && isAttachedToWindow()) {
AutofillManager afm = getAutofillManager();
if (afm != null) {
- if (enter && hasWindowFocus() && isFocused()) {
+ if (enter && hasWindowFocus() && isFocused() && isVisibleToUser()) {
afm.notifyViewEntered(this);
} else if (!hasWindowFocus() || !isFocused()) {
afm.notifyViewExited(this);
@@ -7237,44 +7237,53 @@
RectF position = mAttachInfo.mTmpTransformRect;
position.set(0, 0, mRight - mLeft, mBottom - mTop);
+ mapRectFromViewToScreenCoords(position, clipToParent);
+ outRect.set(Math.round(position.left), Math.round(position.top),
+ Math.round(position.right), Math.round(position.bottom));
+ }
+ /**
+ * Map a rectangle from view-relative coordinates to screen-relative coordinates
+ *
+ * @param rect The rectangle to be mapped
+ * @param clipToParent Whether to clip child bounds to the parent ones.
+ * @hide
+ */
+ public void mapRectFromViewToScreenCoords(RectF rect, boolean clipToParent) {
if (!hasIdentityMatrix()) {
- getMatrix().mapRect(position);
+ getMatrix().mapRect(rect);
}
- position.offset(mLeft, mTop);
+ rect.offset(mLeft, mTop);
ViewParent parent = mParent;
while (parent instanceof View) {
View parentView = (View) parent;
- position.offset(-parentView.mScrollX, -parentView.mScrollY);
+ rect.offset(-parentView.mScrollX, -parentView.mScrollY);
if (clipToParent) {
- position.left = Math.max(position.left, 0);
- position.top = Math.max(position.top, 0);
- position.right = Math.min(position.right, parentView.getWidth());
- position.bottom = Math.min(position.bottom, parentView.getHeight());
+ rect.left = Math.max(rect.left, 0);
+ rect.top = Math.max(rect.top, 0);
+ rect.right = Math.min(rect.right, parentView.getWidth());
+ rect.bottom = Math.min(rect.bottom, parentView.getHeight());
}
if (!parentView.hasIdentityMatrix()) {
- parentView.getMatrix().mapRect(position);
+ parentView.getMatrix().mapRect(rect);
}
- position.offset(parentView.mLeft, parentView.mTop);
+ rect.offset(parentView.mLeft, parentView.mTop);
parent = parentView.mParent;
}
if (parent instanceof ViewRootImpl) {
ViewRootImpl viewRootImpl = (ViewRootImpl) parent;
- position.offset(0, -viewRootImpl.mCurScrollY);
+ rect.offset(0, -viewRootImpl.mCurScrollY);
}
- position.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
-
- outRect.set(Math.round(position.left), Math.round(position.top),
- Math.round(position.right), Math.round(position.bottom));
+ rect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
}
/**
@@ -7295,7 +7304,7 @@
* fills in all data that can be inferred from the view itself.
*/
public void onProvideStructure(ViewStructure structure) {
- onProvideStructureForAssistOrAutofill(structure, false);
+ onProvideStructureForAssistOrAutofill(structure, false, 0);
}
/**
@@ -7309,6 +7318,9 @@
* <li>It must set fields such {@link ViewStructure#setText(CharSequence)},
* {@link ViewStructure#setAutofillOptions(CharSequence[])},
* or {@link ViewStructure#setWebDomain(String)}.
+ * <li> The {@code left} and {@code top} values set in
+ * {@link ViewStructure#setDimens(int, int, int, int, int, int)} need to be relative to the next
+ * {@link ViewGroup#isImportantForAutofill() included} parent in the structure.
* </ul>
*
* @param structure Fill in with structured view data. The default implementation
@@ -7317,12 +7329,12 @@
*
* @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
*/
- public void onProvideAutofillStructure(ViewStructure structure, int flags) {
- onProvideStructureForAssistOrAutofill(structure, true);
+ public void onProvideAutofillStructure(ViewStructure structure, @AutofillFlags int flags) {
+ onProvideStructureForAssistOrAutofill(structure, true, flags);
}
private void onProvideStructureForAssistOrAutofill(ViewStructure structure,
- boolean forAutofill) {
+ boolean forAutofill, @AutofillFlags int flags) {
final int id = mID;
if (id != NO_ID && !isViewIdGenerated(id)) {
String pkg, type, entry;
@@ -7350,11 +7362,37 @@
}
}
- structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop);
- if (!hasIdentityMatrix()) {
- structure.setTransformation(getMatrix());
+ int ignoredParentLeft = 0;
+ int ignoredParentTop = 0;
+ if (forAutofill && (flags & AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) == 0) {
+ View parentGroup = null;
+
+ ViewParent viewParent = getParent();
+ if (viewParent instanceof View) {
+ parentGroup = (View) viewParent;
+ }
+
+ while (parentGroup != null && !parentGroup.isImportantForAutofill()) {
+ ignoredParentLeft += parentGroup.mLeft;
+ ignoredParentTop += parentGroup.mTop;
+
+ viewParent = parentGroup.getParent();
+ if (viewParent instanceof View) {
+ parentGroup = (View) viewParent;
+ } else {
+ break;
+ }
+ }
}
- structure.setElevation(getZ());
+
+ structure.setDimens(ignoredParentLeft + mLeft, ignoredParentTop + mTop, mScrollX, mScrollY,
+ mRight - mLeft, mBottom - mTop);
+ if (!forAutofill) {
+ if (!hasIdentityMatrix()) {
+ structure.setTransformation(getMatrix());
+ }
+ structure.setElevation(getZ());
+ }
structure.setVisibility(getVisibility());
structure.setEnabled(isEnabled());
if (isClickable()) {
@@ -7434,10 +7472,15 @@
* <li>Call {@link AutofillManager#cancel()} ()} when the autofill context
* of the view structure changed and you want the current autofill interaction if such
* to be cancelled.
+ * <li> The {@code left} and {@code top} values set in
+ * {@link ViewStructure#setDimens(int, int, int, int, int, int)} need to be relative to the next
+ * {@link ViewGroup#isImportantForAutofill() included} parent in the structure.
* </ol>
*
* @param structure Fill in with structured view data.
- * @param flags optional flags (currently {@code 0}).
+ * @param flags optional flags.
+ *
+ * @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
*/
public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
}
@@ -7695,7 +7738,8 @@
}
private boolean isAutofillable() {
- return getAutofillType() != AUTOFILL_TYPE_NONE && isImportantForAutofill();
+ return getAutofillType() != AUTOFILL_TYPE_NONE && isImportantForAutofill()
+ && getAccessibilityViewId() > LAST_APP_ACCESSIBILITY_ID;
}
private void populateVirtualStructure(ViewStructure structure,
@@ -7760,7 +7804,7 @@
* {@link #onProvideVirtualStructure}.
*/
public void dispatchProvideStructure(ViewStructure structure) {
- dispatchProvideStructureForAssistOrAutofill(structure, false);
+ dispatchProvideStructureForAssistOrAutofill(structure, false, 0);
}
/**
@@ -7793,16 +7837,15 @@
*/
public void dispatchProvideAutofillStructure(@NonNull ViewStructure structure,
@AutofillFlags int flags) {
- dispatchProvideStructureForAssistOrAutofill(structure, true);
+ dispatchProvideStructureForAssistOrAutofill(structure, true, flags);
}
private void dispatchProvideStructureForAssistOrAutofill(ViewStructure structure,
- boolean forAutofill) {
+ boolean forAutofill, @AutofillFlags int flags) {
if (forAutofill) {
structure.setAutofillId(getAutofillId());
- // NOTE: flags are not currently supported, hence 0
- onProvideAutofillStructure(structure, 0);
- onProvideAutofillVirtualStructure(structure, 0);
+ onProvideAutofillStructure(structure, flags);
+ onProvideAutofillVirtualStructure(structure, flags);
} else if (!isAssistBlocked()) {
onProvideStructure(structure);
onProvideVirtualStructure(structure);
@@ -20419,9 +20462,10 @@
@Nullable private Drawable getAutofilledDrawable() {
// Lazily load the isAutofilled drawable.
if (mAttachInfo.mAutofilledDrawable == null) {
- TypedArray a = mContext.getTheme().obtainStyledAttributes(AUTOFILL_HIGHLIGHT_ATTR);
+ Context rootContext = getRootView().getContext();
+ TypedArray a = rootContext.getTheme().obtainStyledAttributes(AUTOFILL_HIGHLIGHT_ATTR);
int attributeResourceId = a.getResourceId(0, 0);
- mAttachInfo.mAutofilledDrawable = mContext.getDrawable(attributeResourceId);
+ mAttachInfo.mAutofilledDrawable = rootContext.getDrawable(attributeResourceId);
a.recycle();
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index a432d30..6dd8ecf 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -480,13 +480,6 @@
public void onWindowFocusChanged(boolean hasFocus);
/**
- * @hide
- */
- default void onBeforeAttachedToWindow() {
- // empty
- }
-
- /**
* Called when the window has been attached to the window manager.
* See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
* for more information.
diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java
index 7018529..02c8945 100644
--- a/core/java/android/view/WindowCallbackWrapper.java
+++ b/core/java/android/view/WindowCallbackWrapper.java
@@ -109,11 +109,6 @@
}
@Override
- public void onBeforeAttachedToWindow() {
- mWrapped.onBeforeAttachedToWindow();
- }
-
- @Override
public void onAttachedToWindow() {
mWrapped.onAttachedToWindow();
}
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index 143c49a..604e985 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -309,6 +309,13 @@
final int oldChildCount = oldInfo.getChildCount();
for (int i = 0; i < oldChildCount; i++) {
+ if (nodes.get(sourceId) == null) {
+ // We've removed (and thus recycled) this node because it was its own
+ // ancestor (the app gave us bad data), we can't continue using it.
+ // Clear the cache for this window and give up on adding the node.
+ clearNodesForWindowLocked(windowId);
+ return;
+ }
final long oldChildId = oldInfo.getChildId(i);
// If the child is no longer present, remove the sub-tree.
if (newChildrenIds == null || newChildrenIds.indexOf(oldChildId) < 0) {
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index f8a13a3..79c81b2 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -630,7 +630,8 @@
public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800;
/**
- * Represents the event of scrolling a view.
+ * Represents the event of scrolling a view. This event type is generally not sent directly.
+ * @see View#onScrollChanged(int, int, int, int)
*/
public static final int TYPE_VIEW_SCROLLED = 0x00001000;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index d0133ed..c7151db 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -28,7 +28,6 @@
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Bundle;
-import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
@@ -38,7 +37,6 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -47,6 +45,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -188,11 +187,11 @@
boolean autofillCallbackRequestHideFillUi();
/**
- * Checks if the view is currently attached and visible.
+ * Checks if views are currently attached and visible.
*
- * @return {@code true} iff the view is attached or visible
+ * @return And array with {@code true} iff the view is attached or visible
*/
- boolean getViewVisibility(int viewId);
+ @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
/**
* Checks is the client is currently visible as understood by autofill.
@@ -200,6 +199,15 @@
* @return {@code true} if the client is currently visible
*/
boolean isVisibleForAutofill();
+
+ /**
+ * Find views by traversing the hierarchies of the client.
+ *
+ * @param viewIds The accessibility ids of the views to find
+ *
+ * @return And array containing the views, or {@code null} if not found
+ */
+ @NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds);
}
/**
@@ -259,30 +267,6 @@
}
/**
- * Set window future popup windows should be attached to.
- *
- * @param windowToken The window the popup windows should be attached to
- *
- * {@hide}
- */
- public void onAttachedToWindow(@NonNull IBinder windowToken) {
- if (!hasAutofillFeature()) {
- return;
- }
- synchronized (mLock) {
- if (mSessionId == NO_SESSION) {
- return;
- }
-
- try {
- mService.setWindow(mSessionId, windowToken);
- } catch (RemoteException e) {
- Log.e(TAG, "Could not attach window to session " + mSessionId);
- }
- }
- }
-
- /**
* Called once the client becomes visible.
*
* @see AutofillClient#isVisibleForAutofill()
@@ -292,7 +276,7 @@
public void onVisibleForAutofill() {
synchronized (mLock) {
if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
- mTrackedViews.onVisibleForAutofill();
+ mTrackedViews.onVisibleForAutofillLocked();
}
}
}
@@ -406,7 +390,7 @@
if (mSessionId == NO_SESSION) {
// Starts new session.
- startSessionLocked(id, view.getWindowToken(), null, value, flags);
+ startSessionLocked(id, null, value, flags);
} else {
// Update focus on existing session.
updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
@@ -484,7 +468,7 @@
if (mSessionId == NO_SESSION) {
// Starts new session.
- startSessionLocked(id, view.getWindowToken(), bounds, null, flags);
+ startSessionLocked(id, bounds, null, flags);
} else {
// Update focus on existing session.
updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
@@ -725,15 +709,15 @@
return new AutofillId(parent.getAccessibilityViewId(), childId);
}
- private void startSessionLocked(@NonNull AutofillId id, @NonNull IBinder windowToken,
- @NonNull Rect bounds, @NonNull AutofillValue value, int flags) {
+ private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
+ @NonNull AutofillValue value, int flags) {
if (sVerbose) {
Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
+ ", flags=" + flags);
}
try {
- mSessionId = mService.startSession(mContext.getActivityToken(), windowToken,
+ mSessionId = mService.startSession(mContext.getActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, mContext.getOpPackageName());
final AutofillClient client = getClientLocked();
@@ -855,9 +839,9 @@
}
}
- private void requestShowFillUi(int sessionId, IBinder windowToken, AutofillId id, int width,
- int height, Rect anchorBounds, IAutofillWindowPresenter presenter) {
- final View anchor = findAchorView(windowToken, id);
+ private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
+ Rect anchorBounds, IAutofillWindowPresenter presenter) {
+ final View anchor = findView(id);
if (anchor == null) {
return;
}
@@ -930,27 +914,27 @@
}
}
- private void autofill(int sessionId, IBinder windowToken, List<AutofillId> ids,
- List<AutofillValue> values) {
+ private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
synchronized (mLock) {
if (sessionId != mSessionId) {
return;
}
- final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
- if (root == null) {
+ final AutofillClient client = getClientLocked();
+ if (client == null) {
return;
}
final int itemCount = ids.size();
int numApplied = 0;
ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
+ final View[] views = client.findViewsByAccessibilityIdTraversal(getViewIds(ids));
for (int i = 0; i < itemCount; i++) {
final AutofillId id = ids.get(i);
final AutofillValue value = values.get(i);
final int viewId = id.getViewId();
- final View view = root.findViewByAccessibilityIdTraversal(viewId);
+ final View view = views[i];
if (view == null) {
Log.w(TAG, "autofill(): no View with id " + viewId);
continue;
@@ -1022,8 +1006,11 @@
}
}
- private void requestHideFillUi(int sessionId, IBinder windowToken, AutofillId id) {
- final View anchor = findAchorView(windowToken, id);
+ private void requestHideFillUi(int sessionId, AutofillId id) {
+ final View anchor = findView(id);
+ if (anchor == null) {
+ return;
+ }
AutofillCallback callback = null;
synchronized (mLock) {
@@ -1049,8 +1036,11 @@
}
}
- private void notifyNoFillUi(int sessionId, IBinder windowToken, AutofillId id) {
- final View anchor = findAchorView(windowToken, id);
+ private void notifyNoFillUi(int sessionId, AutofillId id) {
+ final View anchor = findView(id);
+ if (anchor == null) {
+ return;
+ }
AutofillCallback callback = null;
synchronized (mLock) {
@@ -1070,18 +1060,38 @@
}
}
- private View findAchorView(IBinder windowToken, AutofillId id) {
- final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
- if (root == null) {
- Log.w(TAG, "no window with token " + windowToken);
+ /**
+ * Get an array of viewIds from a List of {@link AutofillId}.
+ *
+ * @param autofillIds The autofill ids to convert
+ *
+ * @return The array of viewIds.
+ */
+ @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
+ final int numIds = autofillIds.size();
+ final int[] viewIds = new int[numIds];
+ for (int i = 0; i < numIds; i++) {
+ viewIds[i] = autofillIds.get(i).getViewId();
+ }
+
+ return viewIds;
+ }
+
+ /**
+ * Find a single view by its id.
+ *
+ * @param autofillId The autofill id of the view
+ *
+ * @return The view or {@code null} if view was not found
+ */
+ private View findView(@NonNull AutofillId autofillId) {
+ final AutofillClient client = getClientLocked();
+
+ if (client == null) {
return null;
}
- final View view = root.findViewByAccessibilityIdTraversal(id.getViewId());
- if (view == null) {
- Log.w(TAG, "no view with id " + id);
- return null;
- }
- return view;
+
+ return client.findViewsByAccessibilityIdTraversal(new int[]{autofillId.getViewId()})[0];
}
/** @hide */
@@ -1160,22 +1170,26 @@
*
* @param trackedIds The views to be tracked
*/
- TrackedViews(@NonNull List<AutofillId> trackedIds) {
+ TrackedViews(List<AutofillId> trackedIds) {
mVisibleTrackedIds = null;
mInvisibleTrackedIds = null;
AutofillClient client = getClientLocked();
- if (trackedIds != null) {
- int numIds = trackedIds.size();
+ if (trackedIds != null && client != null) {
+ final boolean[] isVisible;
+
+ if (client.isVisibleForAutofill()) {
+ isVisible = client.getViewVisibility(getViewIds(trackedIds));
+ } else {
+ // All false
+ isVisible = new boolean[trackedIds.size()];
+ }
+
+ final int numIds = trackedIds.size();
for (int i = 0; i < numIds; i++) {
- AutofillId id = trackedIds.get(i);
+ final AutofillId id = trackedIds.get(i);
- boolean isVisible = true;
- if (client != null && client.isVisibleForAutofill()) {
- isVisible = client.getViewVisibility(id.getViewId());
- }
-
- if (isVisible) {
+ if (isVisible[i]) {
mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
} else {
mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
@@ -1233,16 +1247,23 @@
*
* @see AutofillClient#isVisibleForAutofill()
*/
- void onVisibleForAutofill() {
- // The visibility of the views might have changed while the client was not started,
+ void onVisibleForAutofillLocked() {
+ // The visibility of the views might have changed while the client was not be visible,
// hence update the visibility state for all views.
AutofillClient client = getClientLocked();
ArraySet<AutofillId> updatedVisibleTrackedIds = null;
ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
if (client != null) {
if (mInvisibleTrackedIds != null) {
- for (AutofillId id : mInvisibleTrackedIds) {
- if (client.getViewVisibility(id.getViewId())) {
+ final ArrayList<AutofillId> orderedInvisibleIds =
+ new ArrayList<>(mInvisibleTrackedIds);
+ final boolean[] isVisible = client.getViewVisibility(
+ getViewIds(orderedInvisibleIds));
+
+ final int numInvisibleTrackedIds = orderedInvisibleIds.size();
+ for (int i = 0; i < numInvisibleTrackedIds; i++) {
+ final AutofillId id = orderedInvisibleIds.get(i);
+ if (isVisible[i]) {
updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
if (sDebug) {
@@ -1255,8 +1276,16 @@
}
if (mVisibleTrackedIds != null) {
- for (AutofillId id : mVisibleTrackedIds) {
- if (client.getViewVisibility(id.getViewId())) {
+ final ArrayList<AutofillId> orderedVisibleIds =
+ new ArrayList<>(mVisibleTrackedIds);
+ final boolean[] isVisible = client.getViewVisibility(
+ getViewIds(orderedVisibleIds));
+
+ final int numVisibleTrackedIds = orderedVisibleIds.size();
+ for (int i = 0; i < numVisibleTrackedIds; i++) {
+ final AutofillId id = orderedVisibleIds.get(i);
+
+ if (isVisible[i]) {
updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
} else {
updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
@@ -1355,12 +1384,11 @@
}
@Override
- public void autofill(int sessionId, IBinder windowToken, List<AutofillId> ids,
- List<AutofillValue> values) {
+ public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
afm.mContext.getMainThreadHandler().post(
- () -> afm.autofill(sessionId, windowToken, ids, values));
+ () -> afm.autofill(sessionId, ids, values));
}
}
@@ -1374,31 +1402,30 @@
}
@Override
- public void requestShowFillUi(int sessionId, IBinder windowToken, AutofillId id,
- int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter) {
+ public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
+ Rect anchorBounds, IAutofillWindowPresenter presenter) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
afm.mContext.getMainThreadHandler().post(
- () -> afm.requestShowFillUi(sessionId, windowToken, id, width, height,
- anchorBounds, presenter));
+ () -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
+ presenter));
}
}
@Override
- public void requestHideFillUi(int sessionId, IBinder windowToken, AutofillId id) {
+ public void requestHideFillUi(int sessionId, AutofillId id) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
afm.mContext.getMainThreadHandler().post(
- () -> afm.requestHideFillUi(sessionId, windowToken, id));
+ () -> afm.requestHideFillUi(sessionId, id));
}
}
@Override
- public void notifyNoFillUi(int sessionId, IBinder windowToken, AutofillId id) {
+ public void notifyNoFillUi(int sessionId, AutofillId id) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.mContext.getMainThreadHandler().post(
- () -> afm.notifyNoFillUi(sessionId, windowToken, id));
+ afm.mContext.getMainThreadHandler().post(() -> afm.notifyNoFillUi(sessionId, id));
}
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index f28d8ba..4193a3c 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -32,12 +32,11 @@
interface IAutoFillManager {
// Returns flags: FLAG_ADD_CLIENT_ENABLED | FLAG_ADD_CLIENT_DEBUG | FLAG_ADD_CLIENT_VERBOSE
int addClient(in IAutoFillManagerClient client, int userId);
- int startSession(IBinder activityToken, IBinder windowToken, in IBinder appCallback,
- in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId,
- boolean hasCallback, int flags, String packageName);
+ int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
+ in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
+ String packageName);
FillEventHistory getFillEventHistory();
boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback);
- void setWindow(int sessionId, in IBinder windowToken);
void updateSession(int sessionId, in AutofillId id, in Rect bounds,
in AutofillValue value, int action, int flags, int userId);
void finishSession(int sessionId, int userId);
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index aef98b7..825d311 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -40,8 +40,7 @@
/**
* Autofills the activity with the contents of a dataset.
*/
- void autofill(int sessionId, in IBinder windowToken, in List<AutofillId> ids,
- in List<AutofillValue> values);
+ void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
/**
* Authenticates a fill response or a data set.
@@ -58,18 +57,18 @@
/**
* Requests showing the fill UI.
*/
- void requestShowFillUi(int sessionId, in IBinder windowToken, in AutofillId id, int width,
- int height, in Rect anchorBounds, in IAutofillWindowPresenter presenter);
+ void requestShowFillUi(int sessionId, in AutofillId id, int width, int height,
+ in Rect anchorBounds, in IAutofillWindowPresenter presenter);
/**
* Requests hiding the fill UI.
*/
- void requestHideFillUi(int sessionId, in IBinder windowToken, in AutofillId id);
+ void requestHideFillUi(int sessionId, in AutofillId id);
/**
* Notifies no fill UI will be shown.
*/
- void notifyNoFillUi(int sessionId, in IBinder windowToken, in AutofillId id);
+ void notifyNoFillUi(int sessionId, in AutofillId id);
/**
* Starts the provided intent sender
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 209ff09..7362c70 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -102,7 +102,9 @@
string, selectionStartIndex, selectionEndIndex);
final int start = startEnd[0];
final int end = startEnd[1];
- if (start >= 0 && end <= string.length() && start <= end) {
+ if (start <= end
+ && start >= 0 && end <= string.length()
+ && start <= selectionStartIndex && end >= selectionEndIndex) {
final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
final SmartSelection.ClassificationResult[] results =
smartSelection.classifyText(
@@ -144,9 +146,6 @@
final TextClassification classificationResult =
createClassificationResult(
results, string.subSequence(startIndex, endIndex));
- // TODO: Added this log for debug only. Remove before release.
- Log.d(LOG_TAG, String.format(
- "Classification type: %s", classificationResult));
return classificationResult;
}
}
@@ -377,11 +376,6 @@
&& Linkify.sUrlMatchFilter.acceptMatch(text, start, end)) {
flag |= SmartSelection.HINT_FLAG_URL;
}
- // TODO: Added this log for debug only. Remove before release.
- Log.d(LOG_TAG, String.format("Email hint: %b",
- (flag & SmartSelection.HINT_FLAG_EMAIL) != 0));
- Log.d(LOG_TAG, String.format("Url hint: %b",
- (flag & SmartSelection.HINT_FLAG_URL) != 0));
return flag;
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 52c82a7..ecb25fe 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -98,7 +98,7 @@
* invoke the Browser application with a URL Intent rather than show it
* with a WebView. For example:
* <pre>
- * Uri uri = Uri.parse("http://www.example.com");
+ * Uri uri = Uri.parse("https://www.example.com");
* Intent intent = new Intent(Intent.ACTION_VIEW, uri);
* startActivity(intent);
* </pre>
@@ -116,7 +116,7 @@
* <pre>
* // Simplest usage: note that an exception will NOT be thrown
* // if there is an error loading this page (see below).
- * webview.loadUrl("http://slashdot.org/");
+ * webview.loadUrl("https://example.com/");
*
* // OR, you can also load from an HTML string:
* String summary = "<html><body>You scored <b>192</b> points.</body></html>";
@@ -175,7 +175,7 @@
* }
* });
*
- * webview.loadUrl("http://developer.android.com/");
+ * webview.loadUrl("https://developer.android.com/");
* </pre>
*
* <h3>Zoom</h3>
@@ -2705,7 +2705,7 @@
* <p>Example2: an IFRAME tag.
*
* <pre class="prettyprint">
- * <iframe src="http://example.com/login"/>
+ * <iframe src="https://example.com/login"/>
* </pre>
*
* <p>Would map to:
@@ -2714,7 +2714,7 @@
* int index = structure.addChildCount(1);
* ViewStructure iframe = structure.newChildFor(index);
* iframe.setHtmlInfo(child.newHtmlInfoBuilder("iframe")
- * .addAttribute("url", "http://example.com/login")
+ * .addAttribute("url", "https://example.com/login")
* .build());
* </pre>
*/
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 7bdd6da..d456989 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -737,6 +737,7 @@
mInputText.setFilters(new InputFilter[] {
new InputTextFilter()
});
+ mInputText.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
mInputText.setImeOptions(EditorInfo.IME_ACTION_DONE);
@@ -770,6 +771,12 @@
if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
+
+ // Should be focusable by default, as the text view whose visibility changes is focusable
+ if (getFocusable() == View.FOCUSABLE_AUTO) {
+ setFocusable(View.FOCUSABLE);
+ setFocusableInTouchMode(true);
+ }
}
@Override
@@ -856,7 +863,7 @@
switch (action) {
case MotionEvent.ACTION_DOWN: {
removeAllCallbacks();
- mInputText.setVisibility(View.INVISIBLE);
+ hideSoftInput();
mLastDownOrMoveEventY = mLastDownEventY = event.getY();
mLastDownEventTime = event.getEventTime();
mIgnoreMoveEvents = false;
@@ -883,11 +890,9 @@
mFlingScroller.forceFinished(true);
mAdjustScroller.forceFinished(true);
} else if (mLastDownEventY < mTopSelectionDividerTop) {
- hideSoftInput();
postChangeCurrentByOneFromLongPress(
false, ViewConfiguration.getLongPressTimeout());
} else if (mLastDownEventY > mBottomSelectionDividerBottom) {
- hideSoftInput();
postChangeCurrentByOneFromLongPress(
true, ViewConfiguration.getLongPressTimeout());
} else {
@@ -1120,6 +1125,7 @@
@Override
public void scrollBy(int x, int y) {
int[] selectorIndices = mSelectorIndices;
+ int startScrollOffset = mCurrentScrollOffset;
if (!mWrapSelectorWheel && y > 0
&& selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
mCurrentScrollOffset = mInitialScrollOffset;
@@ -1147,6 +1153,9 @@
mCurrentScrollOffset = mInitialScrollOffset;
}
}
+ if (startScrollOffset != mCurrentScrollOffset) {
+ onScrollChanged(0, mCurrentScrollOffset, 0, startScrollOffset);
+ }
}
@Override
@@ -1735,7 +1744,10 @@
}
int previous = mValue;
mValue = current;
- updateInputTextView();
+ // If we're flinging, we'll update the text view at the end when it becomes visible
+ if (mScrollState != OnScrollListener.SCROLL_STATE_FLING) {
+ updateInputTextView();
+ }
if (notifyChange) {
notifyChange(previous, current);
}
@@ -1752,7 +1764,7 @@
*/
private void changeValueByOne(boolean increment) {
if (mHasSelectorWheel) {
- mInputText.setVisibility(View.INVISIBLE);
+ hideSoftInput();
if (!moveToFinalScrollerPosition(mFlingScroller)) {
moveToFinalScrollerPosition(mAdjustScroller);
}
@@ -1799,9 +1811,8 @@
*/
private void onScrollerFinished(Scroller scroller) {
if (scroller == mFlingScroller) {
- if (!ensureScrollWheelAdjusted()) {
- updateInputTextView();
- }
+ ensureScrollWheelAdjusted();
+ updateInputTextView();
onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
} else {
if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
@@ -1937,9 +1948,25 @@
*/
String text = (mDisplayedValues == null) ? formatNumber(mValue)
: mDisplayedValues[mValue - mMinValue];
- if (!TextUtils.isEmpty(text) && !text.equals(mInputText.getText().toString())) {
- mInputText.setText(text);
- return true;
+ if (!TextUtils.isEmpty(text)) {
+ CharSequence beforeText = mInputText.getText();
+ if (!text.equals(beforeText.toString())) {
+ mInputText.setText(text);
+ if (mAccessibilityNodeProvider != null) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
+ mInputText.onInitializeAccessibilityEvent(event);
+ mInputText.onPopulateAccessibilityEvent(event);
+ event.setFromIndex(0);
+ event.setRemovedCount(beforeText.length());
+ event.setAddedCount(text.length());
+ event.setBeforeText(beforeText);
+ event.setSource(NumberPicker.this,
+ AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_INPUT);
+ requestSendAccessibilityEvent(NumberPicker.this, event);
+ }
+ return true;
+ }
}
return false;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index eaf1115..bf44f62 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10136,7 +10136,11 @@
if (lineCount <= 1) {
// Simple case: this is a single line.
final CharSequence text = getText();
- structure.setText(text, getSelectionStart(), getSelectionEnd());
+ if (forAutofill) {
+ structure.setText(text);
+ } else {
+ structure.setText(text, getSelectionStart(), getSelectionEnd());
+ }
} else {
// Complex case: multi-line, could be scrolled or within a scroll container
// so some lines are not visible.
@@ -10172,9 +10176,11 @@
if (expandedBottomLine >= lineCount) {
expandedBottomLine = lineCount - 1;
}
+
// Convert lines into character offsets.
int expandedTopChar = layout.getLineStart(expandedTopLine);
int expandedBottomChar = layout.getLineEnd(expandedBottomLine);
+
// Take into account selection -- if there is a selection, we need to expand
// the text we are returning to include that selection.
final int selStart = getSelectionStart();
@@ -10187,48 +10193,57 @@
expandedBottomChar = selEnd;
}
}
+
// Get the text and trim it to the range we are reporting.
CharSequence text = getText();
if (expandedTopChar > 0 || expandedBottomChar < text.length()) {
text = text.subSequence(expandedTopChar, expandedBottomChar);
}
- structure.setText(text, selStart - expandedTopChar, selEnd - expandedTopChar);
- final int[] lineOffsets = new int[bottomLine - topLine + 1];
- final int[] lineBaselines = new int[bottomLine - topLine + 1];
- final int baselineOffset = getBaselineOffset();
- for (int i = topLine; i <= bottomLine; i++) {
- lineOffsets[i - topLine] = layout.getLineStart(i);
- lineBaselines[i - topLine] = layout.getLineBaseline(i) + baselineOffset;
+
+ if (forAutofill) {
+ structure.setText(text);
+ } else {
+ structure.setText(text, selStart - expandedTopChar, selEnd - expandedTopChar);
+
+ final int[] lineOffsets = new int[bottomLine - topLine + 1];
+ final int[] lineBaselines = new int[bottomLine - topLine + 1];
+ final int baselineOffset = getBaselineOffset();
+ for (int i = topLine; i <= bottomLine; i++) {
+ lineOffsets[i - topLine] = layout.getLineStart(i);
+ lineBaselines[i - topLine] = layout.getLineBaseline(i) + baselineOffset;
+ }
+ structure.setTextLines(lineOffsets, lineBaselines);
}
- structure.setTextLines(lineOffsets, lineBaselines);
}
- // Extract style information that applies to the TextView as a whole.
- int style = 0;
- int typefaceStyle = getTypefaceStyle();
- if ((typefaceStyle & Typeface.BOLD) != 0) {
- style |= AssistStructure.ViewNode.TEXT_STYLE_BOLD;
- }
- if ((typefaceStyle & Typeface.ITALIC) != 0) {
- style |= AssistStructure.ViewNode.TEXT_STYLE_ITALIC;
- }
+ if (!forAutofill) {
+ // Extract style information that applies to the TextView as a whole.
+ int style = 0;
+ int typefaceStyle = getTypefaceStyle();
+ if ((typefaceStyle & Typeface.BOLD) != 0) {
+ style |= AssistStructure.ViewNode.TEXT_STYLE_BOLD;
+ }
+ if ((typefaceStyle & Typeface.ITALIC) != 0) {
+ style |= AssistStructure.ViewNode.TEXT_STYLE_ITALIC;
+ }
- // Global styles can also be set via TextView.setPaintFlags().
- int paintFlags = mTextPaint.getFlags();
- if ((paintFlags & Paint.FAKE_BOLD_TEXT_FLAG) != 0) {
- style |= AssistStructure.ViewNode.TEXT_STYLE_BOLD;
- }
- if ((paintFlags & Paint.UNDERLINE_TEXT_FLAG) != 0) {
- style |= AssistStructure.ViewNode.TEXT_STYLE_UNDERLINE;
- }
- if ((paintFlags & Paint.STRIKE_THRU_TEXT_FLAG) != 0) {
- style |= AssistStructure.ViewNode.TEXT_STYLE_STRIKE_THRU;
- }
+ // Global styles can also be set via TextView.setPaintFlags().
+ int paintFlags = mTextPaint.getFlags();
+ if ((paintFlags & Paint.FAKE_BOLD_TEXT_FLAG) != 0) {
+ style |= AssistStructure.ViewNode.TEXT_STYLE_BOLD;
+ }
+ if ((paintFlags & Paint.UNDERLINE_TEXT_FLAG) != 0) {
+ style |= AssistStructure.ViewNode.TEXT_STYLE_UNDERLINE;
+ }
+ if ((paintFlags & Paint.STRIKE_THRU_TEXT_FLAG) != 0) {
+ style |= AssistStructure.ViewNode.TEXT_STYLE_STRIKE_THRU;
+ }
- // TextView does not have its own text background color. A background is either part
- // of the View (and can be any drawable) or a BackgroundColorSpan inside the text.
- structure.setTextStyle(getTextSize(), getCurrentTextColor(),
- AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style);
+ // TextView does not have its own text background color. A background is either part
+ // of the View (and can be any drawable) or a BackgroundColorSpan inside the text.
+ structure.setTextStyle(getTextSize(), getCurrentTextColor(),
+ AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style);
+ }
}
structure.setHint(getHint());
structure.setInputType(getInputType());
@@ -10385,14 +10400,13 @@
positionInfoStartIndex + positionInfoLength,
viewportToContentHorizontalOffset(), viewportToContentVerticalOffset());
CursorAnchorInfo cursorAnchorInfo = builder.setMatrix(null).build();
- int[] locationOnScreen = getLocationOnScreen();
for (int i = 0; i < positionInfoLength; i++) {
int flags = cursorAnchorInfo.getCharacterBoundsFlags(positionInfoStartIndex + i);
if ((flags & FLAG_HAS_VISIBLE_REGION) == FLAG_HAS_VISIBLE_REGION) {
RectF bounds = cursorAnchorInfo
.getCharacterBounds(positionInfoStartIndex + i);
if (bounds != null) {
- bounds.offset(locationOnScreen[0], locationOnScreen[1]);
+ mapRectFromViewToScreenCoords(bounds, true);
boundingRects[i] = bounds;
}
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 99a25fd..373f4bb 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -132,6 +132,7 @@
void noteBleScanStarted(in WorkSource ws);
void noteBleScanStopped(in WorkSource ws);
void noteResetBleScan();
+ void noteBleScanResult(in WorkSource ws);
HealthStatsParceler takeUidSnapshot(int uid);
HealthStatsParceler[] takeUidSnapshots(in int[] uid);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a582c2c..748272600 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -114,7 +114,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 155 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 156 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -4704,6 +4704,14 @@
}
}
+ public void noteBluetoothScanResultFromSourceLocked(WorkSource ws) {
+ final int N = ws.size();
+ for (int i = 0; i < N; i++) {
+ int uid = mapUid(ws.get(i));
+ getUidStatsLocked(uid).noteBluetoothScanResultLocked();
+ }
+ }
+
private void noteWifiRadioApWakeupLocked(final long elapsedRealtimeMillis,
final long uptimeMillis, int uid) {
uid = mapUid(uid);
@@ -5421,6 +5429,7 @@
StopwatchTimer mCameraTurnedOnTimer;
StopwatchTimer mForegroundActivityTimer;
DualTimer mBluetoothScanTimer;
+ Counter mBluetoothScanResultCounter;
int mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
StopwatchTimer[] mProcessStateTimer;
@@ -5864,6 +5873,17 @@
}
}
+ public Counter createBluetoothScanResultCounterLocked() {
+ if (mBluetoothScanResultCounter == null) {
+ mBluetoothScanResultCounter = new Counter(mBsi.mOnBatteryTimeBase);
+ }
+ return mBluetoothScanResultCounter;
+ }
+
+ public void noteBluetoothScanResultLocked() {
+ createBluetoothScanResultCounterLocked().stepAtomic();
+ }
+
@Override
public void noteActivityResumedLocked(long elapsedRealtimeMs) {
// We always start, since we want multiple foreground PIDs to nest
@@ -6017,6 +6037,11 @@
return mBluetoothScanTimer.getSubTimer();
}
+ @Override
+ public Counter getBluetoothScanResultCounter() {
+ return mBluetoothScanResultCounter;
+ }
+
void makeProcessState(int i, Parcel in) {
if (i < 0 || i >= NUM_PROCESS_STATE) return;
@@ -6266,6 +6291,9 @@
active |= !resetTimerIfNotNull(mCameraTurnedOnTimer, false);
active |= !resetTimerIfNotNull(mForegroundActivityTimer, false);
active |= !resetTimerIfNotNull(mBluetoothScanTimer, false);
+ if (mBluetoothScanResultCounter != null) {
+ mBluetoothScanResultCounter.reset(false);
+ }
if (mProcessStateTimer != null) {
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
@@ -6450,6 +6478,10 @@
mBluetoothScanTimer.detach();
mBluetoothScanTimer = null;
}
+ if (mBluetoothScanResultCounter != null) {
+ mBluetoothScanResultCounter.detach();
+ mBluetoothScanResultCounter = null;
+ }
if (mUserActivityCounters != null) {
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
mUserActivityCounters[i].detach();
@@ -6620,6 +6652,12 @@
} else {
out.writeInt(0);
}
+ if (mBluetoothScanResultCounter != null) {
+ out.writeInt(1);
+ mBluetoothScanResultCounter.writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ }
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
if (mProcessStateTimer[i] != null) {
out.writeInt(1);
@@ -6850,6 +6888,11 @@
} else {
mBluetoothScanTimer = null;
}
+ if (in.readInt() != 0) {
+ mBluetoothScanResultCounter = new Counter(mBsi.mOnBatteryTimeBase, in);
+ } else {
+ mBluetoothScanResultCounter = null;
+ }
mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
for (int i = 0; i < NUM_PROCESS_STATE; i++) {
if (in.readInt() != 0) {
@@ -10998,6 +11041,9 @@
if (in.readInt() != 0) {
u.createBluetoothScanTimerLocked().readSummaryFromParcelLocked(in);
}
+ if (in.readInt() != 0) {
+ u.createBluetoothScanResultCounterLocked().readSummaryFromParcelLocked(in);
+ }
u.mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) {
if (in.readInt() != 0) {
@@ -11391,6 +11437,12 @@
} else {
out.writeInt(0);
}
+ if (u.mBluetoothScanResultCounter != null) {
+ out.writeInt(1);
+ u.mBluetoothScanResultCounter.writeSummaryFromParcelLocked(out);
+ } else {
+ out.writeInt(0);
+ }
for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) {
if (u.mProcessStateTimer[i] != null) {
out.writeInt(1);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 80b6b08..1b83708 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1498,7 +1498,6 @@
final Window.Callback cb = mWindow.getCallback();
if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
- cb.onBeforeAttachedToWindow();
cb.onAttachedToWindow();
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 73886a7..91bc681 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -583,4 +583,8 @@
}
return size - leftIdx;
}
+
+ public static @NonNull String[] defeatNullable(@Nullable String[] val) {
+ return (val != null) ? val : EmptyArray.STRING;
+ }
}
diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
index 2c97f8b..cd41f9e 100644
--- a/core/java/com/android/internal/util/NotificationColorUtil.java
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -286,6 +286,38 @@
}
/**
+ * Finds a suitable alpha such that there's enough contrast.
+ *
+ * @param color the color to start searching from.
+ * @param backgroundColor the color to ensure contrast against.
+ * @param minRatio the minimum contrast ratio required.
+ * @return the same color as {@param color} with potentially modified alpha to meet contrast
+ */
+ public static int findAlphaToMeetContrast(int color, int backgroundColor, double minRatio) {
+ int fg = color;
+ int bg = backgroundColor;
+ if (ColorUtilsFromCompat.calculateContrast(fg, bg) >= minRatio) {
+ return color;
+ }
+ int startAlpha = Color.alpha(color);
+ int r = Color.red(color);
+ int g = Color.green(color);
+ int b = Color.blue(color);
+
+ int low = startAlpha, high = 255;
+ for (int i = 0; i < 15 && high - low > 0; i++) {
+ final int alpha = (low + high) / 2;
+ fg = Color.argb(alpha, r, g, b);
+ if (ColorUtilsFromCompat.calculateContrast(fg, bg) > minRatio) {
+ high = alpha;
+ } else {
+ low = alpha;
+ }
+ }
+ return Color.argb(high, r, g, b);
+ }
+
+ /**
* Finds a suitable color such that there's enough contrast.
*
* @param color the color to start searching from.
@@ -373,19 +405,19 @@
* color for the Notification's action and header text.
*
* @param notificationColor the color of the notification or {@link Notification#COLOR_DEFAULT}
+ * @param backgroundColor the background color to ensure the contrast against.
* @return a color of the same hue with enough contrast against the backgrounds.
*/
- public static int resolveContrastColor(Context context, int notificationColor) {
+ public static int resolveContrastColor(Context context, int notificationColor,
+ int backgroundColor) {
final int resolvedColor = resolveColor(context, notificationColor);
final int actionBg = context.getColor(
com.android.internal.R.color.notification_action_list);
- final int notiBg = context.getColor(
- com.android.internal.R.color.notification_material_background_color);
int color = resolvedColor;
color = NotificationColorUtil.ensureLargeTextContrast(color, actionBg);
- color = NotificationColorUtil.ensureTextContrast(color, notiBg);
+ color = NotificationColorUtil.ensureTextContrast(color, backgroundColor);
if (color != resolvedColor) {
if (DEBUG){
@@ -394,7 +426,7 @@
+ " and %s (over background) by changing #%s to %s",
context.getPackageName(),
NotificationColorUtil.contrastChange(resolvedColor, color, actionBg),
- NotificationColorUtil.contrastChange(resolvedColor, color, notiBg),
+ NotificationColorUtil.contrastChange(resolvedColor, color, backgroundColor),
Integer.toHexString(resolvedColor), Integer.toHexString(color)));
}
}
@@ -502,6 +534,13 @@
}
/**
+ * Composite two potentially translucent colors over each other and returns the result.
+ */
+ public static int compositeColors(int foreground, int background) {
+ return ColorUtilsFromCompat.compositeColors(foreground, background);
+ }
+
+ /**
* Framework copy of functions needed from android.support.v4.graphics.ColorUtils.
*/
private static class ColorUtilsFromCompat {
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index fc90fb3..9ad57b1 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -41,9 +41,6 @@
namespace android {
-// Must be same with Java constant in Typeface.Builder. See Typeface.java
-constexpr jint RESOLVE_BY_FONT_TABLE = -1;
-
struct NativeFamilyBuilder {
NativeFamilyBuilder(uint32_t langId, int variant)
: langId(langId), variant(variant), allowUnsupportedFont(false) {}
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 86c97a1..eb2ca5d 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -30,14 +30,14 @@
static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandle, jint style) {
Typeface* family = reinterpret_cast<Typeface*>(familyHandle);
- Typeface* face = Typeface::createFromTypeface(family, (SkTypeface::Style)style);
+ Typeface* face = Typeface::createRelative(family, (SkTypeface::Style)style);
// TODO: the following logic shouldn't be necessary, the above should always succeed.
// Try to find the closest matching font, using the standard heuristic
if (NULL == face) {
- face = Typeface::createFromTypeface(family, (SkTypeface::Style)(style ^ SkTypeface::kItalic));
+ face = Typeface::createRelative(family, (SkTypeface::Style)(style ^ SkTypeface::kItalic));
}
for (int i = 0; NULL == face && i < 4; i++) {
- face = Typeface::createFromTypeface(family, (SkTypeface::Style)i);
+ face = Typeface::createRelative(family, (SkTypeface::Style)i);
}
return reinterpret_cast<jlong>(face);
}
@@ -45,8 +45,7 @@
static jlong Typeface_createFromTypefaceWithExactStyle(JNIEnv* env, jobject, jlong nativeInstance,
jint weight, jboolean italic) {
Typeface* baseTypeface = reinterpret_cast<Typeface*>(nativeInstance);
- return reinterpret_cast<jlong>(
- Typeface::createFromTypefaceWithStyle(baseTypeface, weight, italic));
+ return reinterpret_cast<jlong>(Typeface::createAbsolute(baseTypeface, weight, italic));
}
static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlong familyHandle,
@@ -68,7 +67,7 @@
static jlong Typeface_createWeightAlias(JNIEnv* env, jobject, jlong familyHandle, jint weight) {
Typeface* family = reinterpret_cast<Typeface*>(familyHandle);
- Typeface* face = Typeface::createWeightAlias(family, weight);
+ Typeface* face = Typeface::createWithDifferentBaseWeight(family, weight);
return reinterpret_cast<jlong>(face);
}
@@ -82,9 +81,9 @@
return face->fSkiaStyle;
}
-static jint Typeface_getBaseWeight(JNIEnv* env, jobject obj, jlong faceHandle) {
+static jint Typeface_getWeight(JNIEnv* env, jobject obj, jlong faceHandle) {
Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
- return face->fBaseWeight;
+ return face->fStyle.getWeight() * 100;
}
static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray,
@@ -134,7 +133,7 @@
{ "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias },
{ "nativeUnref", "(J)V", (void*)Typeface_unref },
{ "nativeGetStyle", "(J)I", (void*)Typeface_getStyle },
- { "nativeGetBaseWeight", "(J)I", (void*)Typeface_getBaseWeight },
+ { "nativeGetWeight", "(J)I", (void*)Typeface_getWeight },
{ "nativeCreateFromArray", "([JII)J",
(void*)Typeface_createFromArray },
{ "nativeSetDefault", "(J)V", (void*)Typeface_setDefault },
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 033f2df..fa9379e 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -23,23 +23,34 @@
#include <JNIHelp.h>
#include <vintf/VintfObject.h>
+#include <vintf/parse_string.h>
#include <vintf/parse_xml.h>
#include "core_jni_helpers.h"
static jclass gString;
+static jclass gHashMapClazz;
+static jmethodID gHashMapInit;
+static jmethodID gHashMapPut;
namespace android {
+using vintf::HalManifest;
+using vintf::SchemaType;
using vintf::VintfObject;
+using vintf::XmlConverter;
+using vintf::Vndk;
using vintf::gHalManifestConverter;
using vintf::gCompatibilityMatrixConverter;
-using vintf::XmlConverter;
+using vintf::to_string;
-static inline jobjectArray toJavaStringArray(JNIEnv* env, const std::vector<std::string>& v) {
+template<typename V>
+static inline jobjectArray toJavaStringArray(JNIEnv* env, const V& v) {
+ size_t i;
+ typename V::const_iterator it;
jobjectArray ret = env->NewObjectArray(v.size(), gString, NULL /* init element */);
- for (size_t i = 0; i < v.size(); ++i) {
- env->SetObjectArrayElement(ret, i, env->NewStringUTF(v[i].c_str()));
+ for (i = 0, it = v.begin(); it != v.end(); ++i, ++it) {
+ env->SetObjectArrayElement(ret, i, env->NewStringUTF(it->c_str()));
}
return ret;
}
@@ -55,7 +66,18 @@
}
}
-static jobjectArray android_os_VintfObject_report(JNIEnv* env, jclass clazz)
+static void tryAddHalNamesAndVersions(const HalManifest *manifest,
+ const std::string& description,
+ std::set<std::string> *output) {
+ if (manifest == nullptr) {
+ LOG(WARNING) << __FUNCTION__ << "Cannot get " << description;
+ } else {
+ auto names = manifest->getHalNamesAndVersions();
+ output->insert(names.begin(), names.end());
+ }
+}
+
+static jobjectArray android_os_VintfObject_report(JNIEnv* env, jclass)
{
std::vector<std::string> cStrings;
@@ -71,7 +93,7 @@
return toJavaStringArray(env, cStrings);
}
-static jint android_os_VintfObject_verify(JNIEnv *env, jclass clazz, jobjectArray packageInfo) {
+static jint android_os_VintfObject_verify(JNIEnv* env, jclass, jobjectArray packageInfo) {
size_t count = env->GetArrayLength(packageInfo);
std::vector<std::string> cPackageInfo{count};
for (size_t i = 0; i < count; ++i) {
@@ -84,20 +106,60 @@
return status;
}
+static jobjectArray android_os_VintfObject_getHalNamesAndVersions(JNIEnv* env, jclass) {
+ std::set<std::string> halNames;
+ tryAddHalNamesAndVersions(VintfObject::GetDeviceHalManifest(),
+ "device manifest", &halNames);
+ tryAddHalNamesAndVersions(VintfObject::GetFrameworkHalManifest(),
+ "framework manifest", &halNames);
+ return toJavaStringArray(env, halNames);
+}
+
+static jstring android_os_VintfObject_getSepolicyVersion(JNIEnv* env, jclass) {
+ const HalManifest *manifest = VintfObject::GetDeviceHalManifest();
+ if (manifest == nullptr || manifest->type() != SchemaType::DEVICE) {
+ LOG(WARNING) << __FUNCTION__ << "Cannot get device manifest";
+ return nullptr;
+ }
+ std::string cString = to_string(manifest->sepolicyVersion());
+ return env->NewStringUTF(cString.c_str());
+}
+
+static jobject android_os_VintfObject_getVndkSnapshots(JNIEnv* env, jclass) {
+ const HalManifest *manifest = VintfObject::GetFrameworkHalManifest();
+ if (manifest == nullptr || manifest->type() != SchemaType::FRAMEWORK) {
+ LOG(WARNING) << __FUNCTION__ << "Cannot get framework manifest";
+ return nullptr;
+ }
+ jobject jMap = env->NewObject(gHashMapClazz, gHashMapInit);
+ for (const Vndk &vndk : manifest->vndks()) {
+ std::string key = to_string(vndk.versionRange());
+ env->CallObjectMethod(jMap, gHashMapPut,
+ env->NewStringUTF(key.c_str()), toJavaStringArray(env, vndk.libraries()));
+ }
+ return jMap;
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gVintfObjectMethods[] = {
{"report", "()[Ljava/lang/String;", (void*)android_os_VintfObject_report},
{"verify", "([Ljava/lang/String;)I", (void*)android_os_VintfObject_verify},
+ {"getHalNamesAndVersions", "()[Ljava/lang/String;", (void*)android_os_VintfObject_getHalNamesAndVersions},
+ {"getSepolicyVersion", "()Ljava/lang/String;", (void*)android_os_VintfObject_getSepolicyVersion},
+ {"getVndkSnapshots", "()Ljava/util/Map;", (void*)android_os_VintfObject_getVndkSnapshots},
};
-
const char* const kVintfObjectPathName = "android/os/VintfObject";
int register_android_os_VintfObject(JNIEnv* env)
{
gString = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/String"));
+ gHashMapClazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/util/HashMap"));
+ gHashMapInit = GetMethodIDOrDie(env, gHashMapClazz, "<init>", "()V");
+ gHashMapPut = GetMethodIDOrDie(env, gHashMapClazz,
+ "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
return RegisterMethodsOrDie(env, kVintfObjectPathName, gVintfObjectMethods,
NELEM(gVintfObjectMethods));
diff --git a/core/res/res/drawable/sym_def_app_icon.xml b/core/res/res/drawable/sym_def_app_icon.xml
index 0fdb0dd..9c02402 100644
--- a/core/res/res/drawable/sym_def_app_icon.xml
+++ b/core/res/res/drawable/sym_def_app_icon.xml
@@ -2,10 +2,7 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@android:color/white" />
<foreground>
- <inset android:insetLeft="27.7%"
- android:insetTop="27.7%"
- android:insetRight="27.7%"
- android:insetBottom="27.7%">
+ <inset android:inset="27.7%">
<bitmap android:src="@mipmap/sym_def_app_icon"/>
</inset>
</foreground>
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 5e426e8..9c904d1 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -68,19 +68,19 @@
assertEquals(4, fileEntries.length);
FontFileResourceEntry font1 = fileEntries[0];
assertEquals(400, font1.getWeight());
- assertEquals(false, font1.isItalic());
+ assertEquals(0, font1.getItalic());
assertEquals("res/font/samplefont.ttf", font1.getFileName());
FontFileResourceEntry font2 = fileEntries[1];
assertEquals(400, font2.getWeight());
- assertEquals(true, font2.isItalic());
+ assertEquals(1, font2.getItalic());
assertEquals("res/font/samplefont2.ttf", font2.getFileName());
FontFileResourceEntry font3 = fileEntries[2];
assertEquals(800, font3.getWeight());
- assertEquals(false, font3.isItalic());
+ assertEquals(0, font3.getItalic());
assertEquals("res/font/samplefont3.ttf", font3.getFileName());
FontFileResourceEntry font4 = fileEntries[3];
assertEquals(800, font4.getWeight());
- assertEquals(true, font4.isItalic());
+ assertEquals(1, font4.getItalic());
assertEquals("res/font/samplefont4.ttf", font4.getFileName());
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index a977072..ee2f1ca 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -308,6 +308,7 @@
Settings.Global.SETUP_PREPAID_DETECTION_REDIR_HOST,
Settings.Global.SETUP_PREPAID_DETECTION_TARGET_URL,
Settings.Global.SHORTCUT_MANAGER_CONSTANTS,
+ Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
Settings.Global.SHOW_TEMPERATURE_WARNING,
Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
@@ -371,6 +372,8 @@
Settings.Global.WIFI_REENABLE_DELAY_MS,
Settings.Global.WIFI_SAVED_STATE,
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS,
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
Settings.Global.WIFI_SLEEP_POLICY,
Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index 6e41831..8b5cc60 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -16,10 +16,13 @@
package android.text.method;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.InputType;
+import android.util.KeyUtils;
import android.view.KeyEvent;
+import android.widget.EditText;
import android.widget.TextView.BufferType;
import org.junit.Before;
import org.junit.Test;
@@ -33,13 +36,21 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class BackspaceTest extends KeyListenerTestCase {
+public class BackspaceTest {
+ private EditText mTextView;
+
private static final BaseKeyListener mKeyListener = new BaseKeyListener() {
public int getInputType() {
return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
}
};
+ @Before
+ public void setup() {
+ mTextView = new EditText(InstrumentationRegistry.getInstrumentation().getContext());
+ }
+
+
// Sync the state to the TextView and call onKeyDown with KEYCODE_DEL key event.
// Then update the state to the result of TextView.
private void backspace(final EditorState state, int modifiers) {
@@ -47,7 +58,8 @@
mTextView.setKeyListener(mKeyListener);
mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
- final KeyEvent keyEvent = getKey(KeyEvent.KEYCODE_DEL, modifiers);
+ final KeyEvent keyEvent = KeyUtils.generateKeyEvent(
+ KeyEvent.KEYCODE_DEL, KeyEvent.ACTION_DOWN, modifiers);
mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
state.mText = mTextView.getText();
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index 6914e21..c3a5f80 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -16,10 +16,13 @@
package android.text.method;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.InputType;
+import android.util.KeyUtils;
import android.view.KeyEvent;
+import android.widget.EditText;
import android.widget.TextView.BufferType;
import org.junit.Before;
@@ -34,13 +37,20 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ForwardDeleteTest extends KeyListenerTestCase {
+public class ForwardDeleteTest {
+ private EditText mTextView;
+
private static final BaseKeyListener mKeyListener = new BaseKeyListener() {
public int getInputType() {
return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
}
};
+ @Before
+ public void setup() {
+ mTextView = new EditText(InstrumentationRegistry.getInstrumentation().getContext());
+ }
+
// Sync the state to the TextView and call onKeyDown with KEYCODE_FORWARD_DEL key event.
// Then update the state to the result of TextView.
private void forwardDelete(final EditorState state, int modifiers) {
@@ -48,7 +58,8 @@
mTextView.setKeyListener(mKeyListener);
mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
- final KeyEvent keyEvent = getKey(KeyEvent.KEYCODE_FORWARD_DEL, modifiers);
+ final KeyEvent keyEvent = KeyUtils.generateKeyEvent(
+ KeyEvent.KEYCODE_FORWARD_DEL, KeyEvent.ACTION_DOWN, modifiers);
mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
state.mText = mTextView.getText();
diff --git a/core/tests/coretests/src/android/text/method/KeyListenerTestCase.java b/core/tests/coretests/src/android/text/method/KeyListenerTestCase.java
deleted file mode 100644
index 99a0091..0000000
--- a/core/tests/coretests/src/android/text/method/KeyListenerTestCase.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text.method;
-
-import android.app.Instrumentation;
-import android.support.test.InstrumentationRegistry;
-import android.view.KeyEvent;
-import android.widget.EditText;
-
-public abstract class KeyListenerTestCase {
- protected Instrumentation mInstrumentation;
- protected EditText mTextView;
-
- public KeyListenerTestCase() {
- }
-
- protected void setup() {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mTextView = new EditText(mInstrumentation.getContext());
- }
-
- protected static KeyEvent getKey(int keycode, int metaState) {
- long currentTime = System.currentTimeMillis();
- return new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_DOWN, keycode,
- 0 /* repeat */, metaState);
- }
-}
diff --git a/core/tests/coretests/src/android/util/KeyUtils.java b/core/tests/coretests/src/android/util/KeyUtils.java
index b58fda3..593f727 100644
--- a/core/tests/coretests/src/android/util/KeyUtils.java
+++ b/core/tests/coretests/src/android/util/KeyUtils.java
@@ -85,4 +85,19 @@
}
inst.sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER));
}
+
+ /**
+ * Generates a {@link KeyEvent}.
+ *
+ * @param keycode The integer keycode for the event to be generated.
+ * @param keyEventAction The integer {@link KeyEvent} action code.
+ * @param metaState Flags indicating which meta keys are currently pressed.
+ *
+ * @return a new {@link KeyEvent} for current time.
+ */
+ public static KeyEvent generateKeyEvent(int keycode, int keyEventAction, int metaState) {
+ long currentTime = System.currentTimeMillis();
+ return new KeyEvent(currentTime, currentTime, keyEventAction, keycode,
+ 0 /* repeat */, metaState);
+ }
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 742fd60..7b7031b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -40,6 +40,7 @@
public class TextClassificationManagerTest {
private static final LocaleList LOCALES = LocaleList.forLanguageTags("en");
+ private static final String NO_TYPE = null;
private TextClassificationManager mTcm;
private TextClassifier mClassifier;
@@ -102,6 +103,19 @@
}
@Test
+ public void testSmartSelection_withEmoji() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "\uD83D\uDE02 Hello.";
+ String selected = "Hello";
+ int startIndex = text.indexOf(selected);
+ int endIndex = startIndex + selected.length();
+
+ assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES),
+ isTextSelection(startIndex, endIndex, NO_TYPE));
+ }
+
+ @Test
public void testClassifyText() {
if (isTextClassifierDisabled()) return;
@@ -172,12 +186,17 @@
TextSelection selection = (TextSelection) o;
return startIndex == selection.getSelectionStartIndex()
&& endIndex == selection.getSelectionEndIndex()
- && selection.getEntityCount() > 0
- && type.equals(selection.getEntity(0));
+ && typeMatches(selection, type);
}
return false;
}
+ private boolean typeMatches(TextSelection selection, String type) {
+ return type == null
+ || (selection.getEntityCount() > 0
+ && type.trim().equalsIgnoreCase(selection.getEntity(0)));
+ }
+
@Override
public void describeTo(Description description) {
description.appendValue(
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
new file mode 100644
index 0000000..1dced75
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+package com.android.internal.os;
+
+import static android.os.BatteryStats.STATS_SINCE_CHARGED;
+
+import android.os.WorkSource;
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+/**
+ * Test various BatteryStatsImpl noteStart methods.
+ */
+public class BatteryStatsNoteTest extends TestCase{
+ private static final int UID = 10500;
+ private static final WorkSource WS = new WorkSource(UID);
+
+ /** Test that BatteryStatsImpl.Uid.noteBluetoothScanResultLocked. */
+ @SmallTest
+ public void testNoteBluetoothScanResultLocked() throws Exception {
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(new MockClocks());
+ bi.updateTimeBasesLocked(true, true, 0, 0);
+
+ bi.noteBluetoothScanResultFromSourceLocked(WS);
+ bi.noteBluetoothScanResultFromSourceLocked(WS);
+ assertEquals(2,
+ bi.getUidStats().get(UID).getBluetoothScanResultCounter()
+ .getCountLocked(STATS_SINCE_CHARGED));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 57d6934..3a16fcf 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -15,6 +15,7 @@
BatteryStatsUidTest.class,
BatteryStatsSensorTest.class,
BatteryStatsBackgroundStatsTest.class,
+ BatteryStatsNoteTest.class,
})
public class BatteryStatsTests {
}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index abdab39..8a36120 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -23,6 +23,7 @@
import android.annotation.Size;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.StrictMode;
import android.os.Trace;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -600,6 +601,13 @@
src.position(position);
}
+ private void noteHardwareBitmapSlowCall() {
+ if (getConfig() == Config.HARDWARE) {
+ StrictMode.noteSlowCall("Warning: attempt to read pixels from hardware "
+ + "bitmap, which is very slow operation");
+ }
+ }
+
/**
* Tries to make a new bitmap based on the dimensions of this bitmap,
* setting the new bitmap's config to the one specified, and then copying
@@ -618,6 +626,7 @@
if (config == Config.HARDWARE && isMutable) {
throw new IllegalArgumentException("Hardware bitmaps are always immutable");
}
+ noteHardwareBitmapSlowCall();
Bitmap b = nativeCopy(mNativePtr, config.nativeInt, isMutable);
if (b != null) {
b.setPremultiplied(mRequestPremultiplied);
@@ -635,6 +644,7 @@
*/
public Bitmap createAshmemBitmap() {
checkRecycled("Can't copy a recycled bitmap");
+ noteHardwareBitmapSlowCall();
Bitmap b = nativeCopyAshmem(mNativePtr);
if (b != null) {
b.setPremultiplied(mRequestPremultiplied);
@@ -652,6 +662,7 @@
*/
public Bitmap createAshmemBitmap(Config config) {
checkRecycled("Can't copy a recycled bitmap");
+ noteHardwareBitmapSlowCall();
Bitmap b = nativeCopyAshmemConfig(mNativePtr, config.nativeInt);
if (b != null) {
b.setPremultiplied(mRequestPremultiplied);
@@ -772,6 +783,7 @@
boolean isHardware = source.getConfig() == Config.HARDWARE;
if (isHardware) {
+ source.noteHardwareBitmapSlowCall();
source = nativeCopyPreserveInternalConfig(source.mNativePtr);
}
@@ -1218,6 +1230,7 @@
if (quality < 0 || quality > 100) {
throw new IllegalArgumentException("quality must be 0..100");
}
+ StrictMode.noteSlowCall("Compression of a bitmap is slow");
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress");
boolean result = nativeCompress(mNativePtr, format.nativeInt,
quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
@@ -1792,6 +1805,7 @@
*/
public void writeToParcel(Parcel p, int flags) {
checkRecycled("Can't parcel a recycled bitmap");
+ noteHardwareBitmapSlowCall();
if (!nativeWriteToParcel(mNativePtr, mIsMutable, mDensity, p)) {
throw new RuntimeException("native writeToParcel failed");
}
@@ -1838,6 +1852,7 @@
public Bitmap extractAlpha(Paint paint, int[] offsetXY) {
checkRecycled("Can't extractAlpha on a recycled bitmap");
long nativePaint = paint != null ? paint.getNativeInstance() : 0;
+ noteHardwareBitmapSlowCall();
Bitmap bm = nativeExtractAlpha(mNativePtr, nativePaint, offsetXY);
if (bm == null) {
throw new RuntimeException("Failed to extractAlpha on Bitmap");
@@ -1853,6 +1868,8 @@
*/
public boolean sameAs(Bitmap other) {
checkRecycled("Can't call sameAs on a recycled bitmap!");
+ noteHardwareBitmapSlowCall();
+ other.noteHardwareBitmapSlowCall();
if (this == other) return true;
if (other == null) return false;
if (other.isRecycled()) {
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index f38d8d2..5a56f53 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -123,7 +123,7 @@
public static final int BOLD_ITALIC = 3;
private int mStyle = 0;
- private int mBaseWeight = 0;
+ private int mWeight = 0;
// Value for weight and italic. Indicates the value is resolved by font metadata.
// Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
@@ -233,8 +233,7 @@
// TODO: Add ttc and variation font support. (b/37853920)
if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */,
- fontFile.getWeight(), fontFile.isItalic() ? STYLE_ITALIC : STYLE_NORMAL,
- null /* axes */)) {
+ fontFile.getWeight(), fontFile.getItalic(), null /* axes */)) {
return null;
}
}
@@ -545,7 +544,7 @@
return base;
}
- final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mBaseWeight : mWeight;
+ final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mWeight : mWeight;
final boolean italic =
(mItalic == RESOLVE_BY_FONT_TABLE) ? (base.mStyle & ITALIC) != 0 : mItalic == 1;
final int key = weight << 1 | (italic ? 1 : 0);
@@ -883,7 +882,7 @@
native_instance = ni;
mStyle = nativeGetStyle(ni);
- mBaseWeight = nativeGetBaseWeight(ni);
+ mWeight = nativeGetWeight(ni);
}
private static FontFamily makeFamilyFromParsed(FontConfig.Family family,
@@ -1069,7 +1068,7 @@
private static native long nativeCreateWeightAlias(long native_instance, int weight);
private static native void nativeUnref(long native_instance);
private static native int nativeGetStyle(long native_instance);
- private static native int nativeGetBaseWeight(long native_instance);
+ private static native int nativeGetWeight(long native_instance);
private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic);
private static native void nativeSetDefault(long native_instance);
private static native int[] nativeGetSupportedAxes(long native_instance);
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index bc40191..443aa49 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -188,19 +188,19 @@
// Inset attribute may be overridden by more specific attributes.
if (a.hasValue(R.styleable.InsetDrawable_inset)) {
- final InsetValue inset = getInset(a, R.styleable.InsetDrawable_inset, 0);
+ final InsetValue inset = getInset(a, R.styleable.InsetDrawable_inset, new InsetValue());
state.mInsetLeft = inset;
state.mInsetTop = inset;
state.mInsetRight = inset;
state.mInsetBottom = inset;
}
- state.mInsetLeft = getInset(a, R.styleable.InsetDrawable_insetLeft, 0);
- state.mInsetTop = getInset(a, R.styleable.InsetDrawable_insetTop, 0);
- state.mInsetRight = getInset(a, R.styleable.InsetDrawable_insetRight, 0);
- state.mInsetBottom = getInset(a, R.styleable.InsetDrawable_insetBottom, 0);
+ state.mInsetLeft = getInset(a, R.styleable.InsetDrawable_insetLeft, state.mInsetLeft);
+ state.mInsetTop = getInset(a, R.styleable.InsetDrawable_insetTop, state.mInsetTop);
+ state.mInsetRight = getInset(a, R.styleable.InsetDrawable_insetRight, state.mInsetRight);
+ state.mInsetBottom = getInset(a, R.styleable.InsetDrawable_insetBottom, state.mInsetBottom);
}
- private InsetValue getInset(@NonNull TypedArray a, int index, int defaultValue) {
+ private InsetValue getInset(@NonNull TypedArray a, int index, InsetValue defaultValue) {
if (a.hasValue(index)) {
TypedValue tv = a.peekValue(index);
if (tv.type == TypedValue.TYPE_FRACTION) {
@@ -210,10 +210,13 @@
}
return new InsetValue(f, 0);
} else {
- return new InsetValue(0f, a.getDimensionPixelOffset(index, defaultValue));
+ int dimension = a.getDimensionPixelOffset(index, 0);
+ if (dimension != 0) {
+ return new InsetValue(0, dimension);
+ }
}
}
- return new InsetValue();
+ return defaultValue;
}
private void getInsets(Rect out) {
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 41e5af1..c3ef450 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -483,10 +483,10 @@
final int sourceDensity = mVectorState.mDensity;
final int targetDensity = mTargetDensity;
if (targetDensity != sourceDensity) {
- mDpiScaledWidth = Drawable.scaleFromDensity(
- (int) mVectorState.mBaseWidth, sourceDensity, targetDensity, true);
- mDpiScaledHeight = Drawable.scaleFromDensity(
- (int) mVectorState.mBaseHeight,sourceDensity, targetDensity, true);
+ mDpiScaledWidth = Drawable.scaleFromDensity(mVectorState.mBaseWidth, sourceDensity,
+ targetDensity, true);
+ mDpiScaledHeight = Drawable.scaleFromDensity(mVectorState.mBaseHeight,sourceDensity,
+ targetDensity, true);
final int left = Drawable.scaleFromDensity(
opticalInsets.left, sourceDensity, targetDensity, false);
final int right = Drawable.scaleFromDensity(
@@ -497,8 +497,8 @@
opticalInsets.bottom, sourceDensity, targetDensity, false);
mDpiScaledInsets = Insets.of(left, top, right, bottom);
} else {
- mDpiScaledWidth = (int) mVectorState.mBaseWidth;
- mDpiScaledHeight = (int) mVectorState.mBaseHeight;
+ mDpiScaledWidth = mVectorState.mBaseWidth;
+ mDpiScaledHeight = mVectorState.mBaseHeight;
mDpiScaledInsets = opticalInsets;
}
@@ -675,9 +675,9 @@
"<vector> tag requires viewportHeight > 0");
}
- state.mBaseWidth = a.getDimension(
+ state.mBaseWidth = a.getDimensionPixelSize(
R.styleable.VectorDrawable_width, state.mBaseWidth);
- state.mBaseHeight = a.getDimension(
+ state.mBaseHeight = a.getDimensionPixelSize(
R.styleable.VectorDrawable_height, state.mBaseHeight);
if (state.mBaseWidth <= 0) {
@@ -819,8 +819,8 @@
Mode mTintMode = DEFAULT_TINT_MODE;
boolean mAutoMirrored;
- float mBaseWidth = 0;
- float mBaseHeight = 0;
+ int mBaseWidth = 0;
+ int mBaseHeight = 0;
float mViewportWidth = 0;
float mViewportHeight = 0;
Insets mOpticalInsets = Insets.NONE;
@@ -1004,8 +1004,9 @@
}
private void applyDensityScaling(int sourceDensity, int targetDensity) {
- mBaseWidth = Drawable.scaleFromDensity(mBaseWidth, sourceDensity, targetDensity);
- mBaseHeight = Drawable.scaleFromDensity(mBaseHeight, sourceDensity, targetDensity);
+ mBaseWidth = Drawable.scaleFromDensity(mBaseWidth, sourceDensity, targetDensity, true);
+ mBaseHeight = Drawable.scaleFromDensity(mBaseHeight, sourceDensity, targetDensity,
+ true);
final int insetLeft = Drawable.scaleFromDensity(
mOpticalInsets.left, sourceDensity, targetDensity, false);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 5ef49dc..030e845 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -319,6 +319,7 @@
tests/unit/TestUtilsTests.cpp \
tests/unit/TextDropShadowCacheTests.cpp \
tests/unit/TextureCacheTests.cpp \
+ tests/unit/TypefaceTests.cpp \
tests/unit/VectorDrawableTests.cpp \
include $(LOCAL_PATH)/hwui_static_deps.mk
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index d7f75fc..d765584 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -486,7 +486,6 @@
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
if (isHardware()) {
- ALOGW("Warning: attempt to read pixels from hardware bitmap, which is very slow operation");
outBitmap->allocPixels(info());
uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap);
return;
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 4fb4b53..f66bb04 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -36,28 +36,33 @@
#include <minikin/FontFamily.h>
#include <minikin/Layout.h>
#include <utils/Log.h>
+#include <utils/MathUtils.h>
namespace android {
-// This indicates that the passed information should be resolved by OS/2 table.
-// This value must be the same as the android.graphics.Typeface$Builder.RESOLVE_BY_FONT_TABLE.
-constexpr int RESOLVE_BY_FONT_TABLE = -1;
+static SkTypeface::Style computeSkiaStyle(int weight, bool italic) {
+ // This bold detection comes from SkTypeface.h
+ if (weight >= SkFontStyle::kSemiBold_Weight) {
+ return italic ? SkTypeface::kBoldItalic : SkTypeface::kBold;
+ } else {
+ return italic ? SkTypeface::kItalic : SkTypeface::kNormal;
+ }
+}
-// Resolve the 1..10 weight based on base weight and bold flag
-static void resolveStyle(Typeface* typeface) {
+static minikin::FontStyle computeMinikinStyle(int weight, bool italic) {
// TODO: Better to use raw base weight value for font selection instead of dividing by 100.
- int weight = (typeface->fBaseWeight + 50) / 100;
- if (typeface->fSkiaStyle & SkTypeface::kBold) {
- weight += 3;
+ const int minikinWeight = uirenderer::MathUtils::clamp((weight + 50) / 100, 1, 10);
+ return minikin::FontStyle(minikinWeight, italic);
+}
+
+// Resolve the relative weight from the baseWeight and target style.
+static minikin::FontStyle computeRelativeStyle(int baseWeight, SkTypeface::Style relativeStyle) {
+ int weight = baseWeight;
+ if ((relativeStyle & SkTypeface::kBold) != 0) {
+ weight += 300;
}
- if (weight > 10) {
- weight = 10;
- }
- if (weight < 1) {
- weight = 1;
- }
- bool italic = (typeface->fSkiaStyle & SkTypeface::kItalic) != 0;
- typeface->fStyle = minikin::FontStyle(weight, italic);
+ bool italic = (relativeStyle & SkTypeface::kItalic) != 0;
+ return computeMinikinStyle(weight, italic);
}
Typeface* gDefaultTypeface = NULL;
@@ -67,26 +72,26 @@
return src == nullptr ? gDefaultTypeface : src;
}
-Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) {
+Typeface* Typeface::createRelative(Typeface* src, SkTypeface::Style style) {
Typeface* resolvedFace = Typeface::resolveDefault(src);
Typeface* result = new Typeface;
if (result != nullptr) {
result->fFontCollection = resolvedFace->fFontCollection;
- result->fSkiaStyle = style;
result->fBaseWeight = resolvedFace->fBaseWeight;
- resolveStyle(result);
+ result->fSkiaStyle = style;
+ result->fStyle = computeRelativeStyle(result->fBaseWeight, style);
}
return result;
}
-Typeface* Typeface::createFromTypefaceWithStyle(Typeface* base, int weight, bool italic) {
+Typeface* Typeface::createAbsolute(Typeface* base, int weight, bool italic) {
Typeface* resolvedFace = Typeface::resolveDefault(base);
Typeface* result = new Typeface();
if (result != nullptr) {
result->fFontCollection = resolvedFace->fFontCollection;
- result->fBaseWeight = weight;
- result->fStyle = minikin::FontStyle(weight / 100, italic);
- result->fSkiaStyle = resolvedFace->fSkiaStyle;
+ result->fBaseWeight = resolvedFace->fBaseWeight;
+ result->fSkiaStyle = computeSkiaStyle(weight, italic);
+ result->fStyle = computeMinikinStyle(weight, italic);
}
return result;
}
@@ -103,21 +108,23 @@
// So we will reuse the same collection with incrementing reference count.
result->fFontCollection = resolvedFace->fFontCollection;
}
- result->fSkiaStyle = resolvedFace->fSkiaStyle;
+ // Do not update styles.
+ // TODO: We may want to update base weight if the 'wght' is specified.
result->fBaseWeight = resolvedFace->fBaseWeight;
- resolveStyle(result);
+ result->fSkiaStyle = resolvedFace->fSkiaStyle;
+ result->fStyle = resolvedFace->fStyle;
}
return result;
}
-Typeface* Typeface::createWeightAlias(Typeface* src, int weight) {
+Typeface* Typeface::createWithDifferentBaseWeight(Typeface* src, int weight) {
Typeface* resolvedFace = Typeface::resolveDefault(src);
Typeface* result = new Typeface;
if (result != nullptr) {
result->fFontCollection = resolvedFace->fFontCollection;
- result->fSkiaStyle = resolvedFace->fSkiaStyle;
result->fBaseWeight = weight;
- resolveStyle(result);
+ result->fSkiaStyle = resolvedFace->fSkiaStyle;
+ result->fStyle = computeRelativeStyle(weight, result->fSkiaStyle);
}
return result;
}
@@ -160,14 +167,8 @@
}
result->fBaseWeight = weight;
- // This bold detection comes from SkTypefae.h
- const bool isBold = weight >= SkFontStyle::kSemiBold_Weight;
- const bool isItalic = italic == 1;
- // TODO: remove fSkiaStyle
- result->fSkiaStyle = isBold ?
- (isItalic ? SkTypeface::kBoldItalic : SkTypeface::kBold) :
- (isItalic ? SkTypeface::kItalic : SkTypeface::kNormal);
- resolveStyle(result);
+ result->fSkiaStyle = computeSkiaStyle(weight, italic);
+ result->fStyle = computeMinikinStyle(weight, italic);
return result;
}
@@ -197,7 +198,7 @@
Typeface* hwTypeface = new Typeface();
hwTypeface->fFontCollection = collection;
hwTypeface->fSkiaStyle = SkTypeface::kNormal;
- hwTypeface->fBaseWeight = SkFontStyle::kSemiBold_Weight;
+ hwTypeface->fBaseWeight = SkFontStyle::kNormal_Weight;
hwTypeface->fStyle = minikin::FontStyle(4 /* weight */, false /* italic */);
Typeface::setDefault(hwTypeface);
diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h
index e35a7b4..db0b2cd 100644
--- a/libs/hwui/hwui/Typeface.h
+++ b/libs/hwui/hwui/Typeface.h
@@ -27,28 +27,53 @@
namespace android {
-struct ANDROID_API Typeface {
- std::shared_ptr<minikin::FontCollection> fFontCollection;
+// This indicates that the weight or italic information should be resolved by OS/2 table.
+// This value must be the same as the android.graphics.Typeface$Builder.RESOLVE_BY_FONT_TABLE.
+constexpr int RESOLVE_BY_FONT_TABLE = -1;
- // style used for constructing and querying Typeface objects
- SkTypeface::Style fSkiaStyle;
- // base weight in CSS-style units, 100..900
- int fBaseWeight;
+struct ANDROID_API Typeface {
+ public:
+ std::shared_ptr<minikin::FontCollection> fFontCollection;
// resolved style actually used for rendering
minikin::FontStyle fStyle;
+ // style used for constructing and querying Typeface objects
+ SkTypeface::Style fSkiaStyle;
+
static Typeface* resolveDefault(Typeface* src);
- static Typeface* createFromTypeface(Typeface* src, SkTypeface::Style style);
-
- static Typeface* createFromTypefaceWithStyle(Typeface* base, int weight, bool italic);
+ // The following three functions create new Typeface from an existing Typeface with a different
+ // style. There is a base weight concept which is used for calculating relative style from an
+ // existing Typeface.
+ // The createRelative method creates a new Typeface with a style relative to the base Typeface.
+ // For example, if the base Typeface has a base weight of 400 and the desired style is bold, the
+ // resulting Typeface renders the text with a weight of 700. This function doesn't change the
+ // base weight, so even if you create a new Typeface from the bold Typeface specifying bold on
+ // it again, the text is still rendered with a weight of 700.
+ // You can create another base weight Typeface from an existing Typeface with
+ // createWithDifferentBaseWeight. The Typeface created with this function renders the text with
+ // a specified base weight.
+ // The createAbsolute method creates a new Typeface ignoring the base weight.
+ // Here is an example:
+ // Typeface* base = resolveDefault(nullptr); // Usually this has a weight of 400.
+ // Typeface* bold = createRelative(base, Bold); // Rendered with a weight of 700.
+ // Typeface* bold2 = createRelative(bold, Bold); // Rendered with a weight of 700.
+ //
+ // Typeface* boldBase = createWithDifferentBaseWeight(base, 700); // With a weight of 700.
+ // Typeface* boldBold = createRelative(boldBase, Bold); // Rendered with a weight of 1000.
+ //
+ // Typeface* lightBase = createWithDifferentBaseWeight(base, 300); // With a weight of 300.
+ // Typeface* lightBold = createRelative(lightBase, Bold); // Rendered with a weight of 600.
+ //
+ // Typeface* black = createAbsolute(base, 900, false); // Rendered with a weight of 900.
+ static Typeface* createWithDifferentBaseWeight(Typeface* src, int baseweight);
+ static Typeface* createRelative(Typeface* src, SkTypeface::Style desiredStyle);
+ static Typeface* createAbsolute(Typeface* base, int weight, bool italic);
static Typeface* createFromTypefaceWithVariation(Typeface* src,
const std::vector<minikin::FontVariation>& variations);
- static Typeface* createWeightAlias(Typeface* src, int baseweight);
-
static Typeface* createFromFamilies(
std::vector<std::shared_ptr<minikin::FontFamily>>&& families,
int weight, int italic);
@@ -57,6 +82,10 @@
// Sets roboto font as the default typeface for testing purpose.
static void setRobotoTypefaceForTest();
+ private:
+ // base weight in CSS-style units, 1..1000
+ int fBaseWeight;
+
};
}
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
new file mode 100644
index 0000000..c90b6f0
--- /dev/null
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -0,0 +1,418 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <utils/Log.h>
+
+#include "SkFontMgr.h"
+#include "SkStream.h"
+
+#include "hwui/MinikinSkia.h"
+#include "hwui/Typeface.h"
+
+using namespace android;
+
+namespace {
+
+constexpr char kRobotoRegular[] = "/system/fonts/Roboto-Regular.ttf";
+constexpr char kRobotoBold[] = "/system/fonts/Roboto-Bold.ttf";
+constexpr char kRobotoItalic[] = "/system/fonts/Roboto-Italic.ttf";
+constexpr char kRobotoBoldItalic[] = "/system/fonts/Roboto-BoldItalic.ttf";
+
+void unmap(const void* ptr, void* context) {
+ void* p = const_cast<void*>(ptr);
+ size_t len = reinterpret_cast<size_t>(context);
+ munmap(p, len);
+}
+
+std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) {
+ int fd = open(fileName, O_RDONLY);
+ LOG_ALWAYS_FATAL_IF(fd == -1, "Failed to open file %s", fileName);
+ struct stat st = {};
+ LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", fileName);
+ void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ sk_sp<SkData> skData =
+ SkData::MakeWithProc(data, st.st_size, unmap, reinterpret_cast<void*>(st.st_size));
+ std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(skData));
+ sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ sk_sp<SkTypeface> typeface(fm->createFromStream(fontData.release()));
+ LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
+ std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
+ std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
+ return std::make_shared<minikin::FontFamily>(
+ std::vector<minikin::Font>({ minikin::Font(std::move(font), minikin::FontStyle()) }));
+}
+
+std::vector<std::shared_ptr<minikin::FontFamily>> makeSingleFamlyVector(const char* fileName) {
+ return std::vector<std::shared_ptr<minikin::FontFamily>>({ buildFamily(fileName) });
+}
+
+TEST(TypefaceTest, resolveDefault_and_setDefaultTest) {
+ std::unique_ptr<Typeface> regular(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular),
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ EXPECT_EQ(regular.get(), Typeface::resolveDefault(regular.get()));
+
+ Typeface* old = Typeface::resolveDefault(nullptr); // Keep the original to restore it later.
+ ASSERT_NE(nullptr, old);
+
+ Typeface::setDefault(regular.get());
+ EXPECT_EQ(regular.get(), Typeface::resolveDefault(nullptr));
+
+ Typeface::setDefault(old); // Restore to the original.
+}
+
+TEST(TypefaceTest, createWithDifferentBaseWeight) {
+ std::unique_ptr<Typeface> bold(Typeface::createWithDifferentBaseWeight(nullptr, 700));
+ EXPECT_EQ(7, bold->fStyle.getWeight());
+ EXPECT_FALSE(bold->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kNormal, bold->fSkiaStyle);
+
+ std::unique_ptr<Typeface> light(Typeface::createWithDifferentBaseWeight(nullptr, 300));
+ EXPECT_EQ(3, light->fStyle.getWeight());
+ EXPECT_FALSE(light->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kNormal, light->fSkiaStyle);
+}
+
+TEST(TypefaceTest, createRelativeTest_fromRegular) {
+ // In Java, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(nullptr, SkTypeface::kNormal));
+ EXPECT_EQ(4, normal->fStyle.getWeight());
+ EXPECT_FALSE(normal->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+
+ // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD);
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(nullptr, SkTypeface::kBold));
+ EXPECT_EQ(7, bold->fStyle.getWeight());
+ EXPECT_FALSE(bold->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+
+ // In Java, Typeface.create(Typeface.DEFAULT, Typeface.ITALIC);
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(nullptr, SkTypeface::kItalic));
+ EXPECT_EQ(4, italic->fStyle.getWeight());
+ EXPECT_TRUE(italic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+
+ // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
+ std::unique_ptr<Typeface> boldItalic(
+ Typeface::createRelative(nullptr, SkTypeface::kBoldItalic));
+ EXPECT_EQ(7, boldItalic->fStyle.getWeight());
+ EXPECT_TRUE(boldItalic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+}
+
+TEST(TypefaceTest, createRelativeTest_BoldBase) {
+ std::unique_ptr<Typeface> base(Typeface::createWithDifferentBaseWeight(nullptr, 700));
+
+ // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.NORMAL);
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+ EXPECT_EQ(7, normal->fStyle.getWeight());
+ EXPECT_FALSE(normal->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+
+ // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.BOLD);
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+ EXPECT_EQ(10, bold->fStyle.getWeight());
+ EXPECT_FALSE(bold->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+
+ // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.ITALIC);
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+ EXPECT_EQ(7, italic->fStyle.getWeight());
+ EXPECT_TRUE(italic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+
+ // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.BOLD_ITALIC);
+ std::unique_ptr<Typeface>
+ boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+ EXPECT_EQ(10, boldItalic->fStyle.getWeight());
+ EXPECT_TRUE(boldItalic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+}
+
+TEST(TypefaceTest, createRelativeTest_LightBase) {
+ std::unique_ptr<Typeface> base(Typeface::createWithDifferentBaseWeight(nullptr, 300));
+
+ // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.NORMAL);
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+ EXPECT_EQ(3, normal->fStyle.getWeight());
+ EXPECT_FALSE(normal->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+
+ // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.BOLD);
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+ EXPECT_EQ(6, bold->fStyle.getWeight());
+ EXPECT_FALSE(bold->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+
+ // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.ITLIC);
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+ EXPECT_EQ(3, italic->fStyle.getWeight());
+ EXPECT_TRUE(italic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+
+ // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.BOLD_ITALIC);
+ std::unique_ptr<Typeface>
+ boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+ EXPECT_EQ(6, boldItalic->fStyle.getWeight());
+ EXPECT_TRUE(boldItalic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+}
+
+TEST(TypefaceTest, createRelativeTest_fromBoldStyled) {
+ std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, SkTypeface::kBold));
+
+ // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.NORMAL);
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+ EXPECT_EQ(4, normal->fStyle.getWeight());
+ EXPECT_FALSE(normal->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+
+ // In Java Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.BOLD);
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+ EXPECT_EQ(7, bold->fStyle.getWeight());
+ EXPECT_FALSE(bold->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+
+ // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.ITALIC);
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+ EXPECT_EQ(4, normal->fStyle.getWeight());
+ EXPECT_TRUE(italic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+
+ // In Java,
+ // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.BOLD_ITALIC);
+ std::unique_ptr<Typeface>
+ boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+ EXPECT_EQ(7, boldItalic->fStyle.getWeight());
+ EXPECT_TRUE(boldItalic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+}
+
+TEST(TypefaceTest, createRelativeTest_fromItalicStyled) {
+ std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, SkTypeface::kItalic));
+
+ // In Java,
+ // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.NORMAL);
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+ EXPECT_EQ(4, normal->fStyle.getWeight());
+ EXPECT_FALSE(normal->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+
+ // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.BOLD);
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+ EXPECT_EQ(7, bold->fStyle.getWeight());
+ EXPECT_FALSE(bold->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+
+ // In Java,
+ // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.ITALIC);
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+ EXPECT_EQ(4, italic->fStyle.getWeight());
+ EXPECT_TRUE(italic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+
+ // In Java,
+ // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.BOLD_ITALIC);
+ std::unique_ptr<Typeface>
+ boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+ EXPECT_EQ(7, boldItalic->fStyle.getWeight());
+ EXPECT_TRUE(boldItalic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+}
+
+TEST(TypefaceTest, createRelativeTest_fromSpecifiedStyled) {
+ std::unique_ptr<Typeface> base(Typeface::createAbsolute(nullptr, 400, false));
+
+ // In Java,
+ // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
+ // .setWeight(700).setItalic(false).build();
+ // Typeface.create(typeface, Typeface.NORMAL);
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+ EXPECT_EQ(4, normal->fStyle.getWeight());
+ EXPECT_FALSE(normal->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+
+ // In Java,
+ // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
+ // .setWeight(700).setItalic(false).build();
+ // Typeface.create(typeface, Typeface.BOLD);
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+ EXPECT_EQ(7, bold->fStyle.getWeight());
+ EXPECT_FALSE(bold->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+
+ // In Java,
+ // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
+ // .setWeight(700).setItalic(false).build();
+ // Typeface.create(typeface, Typeface.ITALIC);
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+ EXPECT_EQ(4, italic->fStyle.getWeight());
+ EXPECT_TRUE(italic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+
+ // In Java,
+ // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
+ // .setWeight(700).setItalic(false).build();
+ // Typeface.create(typeface, Typeface.BOLD_ITALIC);
+ std::unique_ptr<Typeface>
+ boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+ EXPECT_EQ(7, boldItalic->fStyle.getWeight());
+ EXPECT_TRUE(boldItalic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+}
+
+TEST(TypefaceTest, createAbsolute) {
+ // In Java,
+ // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(false)
+ // .build();
+ std::unique_ptr<Typeface> regular(Typeface::createAbsolute(nullptr, 400, false));
+ EXPECT_EQ(4, regular->fStyle.getWeight());
+ EXPECT_FALSE(regular->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle);
+
+ // In Java,
+ // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(false)
+ // .build();
+ std::unique_ptr<Typeface> bold(Typeface::createAbsolute(nullptr, 700, false));
+ EXPECT_EQ(7, bold->fStyle.getWeight());
+ EXPECT_FALSE(bold->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+
+ // In Java,
+ // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(true)
+ // .build();
+ std::unique_ptr<Typeface> italic(Typeface::createAbsolute(nullptr, 400, true));
+ EXPECT_EQ(4, italic->fStyle.getWeight());
+ EXPECT_TRUE(italic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+
+ // In Java,
+ // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(true)
+ // .build();
+ std::unique_ptr<Typeface> boldItalic(Typeface::createAbsolute(nullptr, 700, true));
+ EXPECT_EQ(7, boldItalic->fStyle.getWeight());
+ EXPECT_TRUE(boldItalic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+
+ // In Java,
+ // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(1100).setItalic(true)
+ // .build();
+ std::unique_ptr<Typeface> over1000(Typeface::createAbsolute(nullptr, 1100, false));
+ EXPECT_EQ(10, over1000->fStyle.getWeight());
+ EXPECT_FALSE(over1000->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBold, over1000->fSkiaStyle);
+}
+
+TEST(TypefaceTest, createFromFamilies_Single) {
+ // In Java, new Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(false).build();
+ std::unique_ptr<Typeface> regular(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), 400, false));
+ EXPECT_EQ(4, regular->fStyle.getWeight());
+ EXPECT_FALSE(regular->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle);
+
+ // In Java, new Typeface.Builder("Roboto-Bold.ttf").setWeight(700).setItalic(false).build();
+ std::unique_ptr<Typeface> bold(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 700, false));
+ EXPECT_EQ(7, bold->fStyle.getWeight());
+ EXPECT_FALSE(bold->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+
+ // In Java, new Typeface.Builder("Roboto-Italic.ttf").setWeight(400).setItalic(true).build();
+ std::unique_ptr<Typeface> italic(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), 400, true));
+ EXPECT_EQ(4, italic->fStyle.getWeight());
+ EXPECT_TRUE(italic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+
+ // In Java,
+ // new Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(700).setItalic(true).build();
+ std::unique_ptr<Typeface> boldItalic(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), 700, true));
+ EXPECT_EQ(7, boldItalic->fStyle.getWeight());
+ EXPECT_TRUE(boldItalic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+
+ // In Java,
+ // new Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(1100).setItalic(false).build();
+ std::unique_ptr<Typeface> over1000(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 1100, false));
+ EXPECT_EQ(10, over1000->fStyle.getWeight());
+ EXPECT_FALSE(over1000->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBold, over1000->fSkiaStyle);
+}
+
+TEST(TypefaceTest, createFromFamilies_Single_resolveByTable) {
+ // In Java, new Typeface.Builder("Roboto-Regular.ttf").build();
+ std::unique_ptr<Typeface> regular(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular),
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ EXPECT_EQ(4, regular->fStyle.getWeight());
+ EXPECT_FALSE(regular->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle);
+
+ // In Java, new Typeface.Builder("Roboto-Bold.ttf").build();
+ std::unique_ptr<Typeface> bold(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold),
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ EXPECT_EQ(7, bold->fStyle.getWeight());
+ EXPECT_FALSE(bold->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+
+ // In Java, new Typeface.Builder("Roboto-Italic.ttf").build();
+ std::unique_ptr<Typeface> italic(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic),
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ EXPECT_EQ(4, italic->fStyle.getWeight());
+ EXPECT_TRUE(italic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+
+ // In Java, new Typeface.Builder("Roboto-BoldItalic.ttf").build();
+ std::unique_ptr<Typeface> boldItalic(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic),
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ EXPECT_EQ(7, boldItalic->fStyle.getWeight());
+ EXPECT_TRUE(boldItalic->fStyle.getItalic());
+ EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+}
+
+TEST(TypefaceTest, createFromFamilies_Family) {
+ std::vector<std::shared_ptr<minikin::FontFamily>> families = {
+ buildFamily(kRobotoRegular), buildFamily(kRobotoBold), buildFamily(kRobotoItalic),
+ buildFamily(kRobotoBoldItalic)
+ };
+ std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(std::move(families),
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ EXPECT_EQ(4, typeface->fStyle.getWeight());
+ EXPECT_FALSE(typeface->fStyle.getItalic());
+}
+
+TEST(TypefaceTest, createFromFamilies_Family_withoutRegular) {
+ std::vector<std::shared_ptr<minikin::FontFamily>> families = {
+ buildFamily(kRobotoBold), buildFamily(kRobotoItalic), buildFamily(kRobotoBoldItalic)
+ };
+ std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(std::move(families),
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ EXPECT_EQ(7, typeface->fStyle.getWeight());
+ EXPECT_FALSE(typeface->fStyle.getItalic());
+}
+
+} // namespace
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index ccdf5ae..c78c99f 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -705,6 +705,8 @@
}
nativeDetachImage(image);
+ si.clearSurfacePlanes();
+ si.mPlanes = null;
si.setDetached(true);
}
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 349c9cb..2b7309f 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -359,28 +359,14 @@
}
ImageReader prevOwner = (ImageReader) image.getOwner();
- // Only do the image attach for PRIVATE format images for now. Do the image
- // copy for other formats. TODO: use attach for other formats to
- // improve the performance, and fall back to copy when attach/detach
- // fails. Right now, detach is guaranteed to fail as the buffer is
- // locked when ImageReader#acquireNextImage is called. See bug 19962027.
- if (image.getFormat() == ImageFormat.PRIVATE) {
- prevOwner.detachImage(image);
- attachAndQueueInputImage(image);
- // This clears the native reference held by the original owner.
- // When this Image is detached later by this ImageWriter, the
- // native memory won't be leaked.
- image.close();
- return;
- } else {
- Image inputImage = dequeueInputImage();
- inputImage.setTimestamp(image.getTimestamp());
- inputImage.setCropRect(image.getCropRect());
- ImageUtils.imageCopy(image, inputImage);
- image.close();
- image = inputImage;
- ownedByMe = true;
- }
+
+ prevOwner.detachImage(image);
+ attachAndQueueInputImage(image);
+ // This clears the native reference held by the original owner.
+ // When this Image is detached later by this ImageWriter, the
+ // native memory won't be leaked.
+ image.close();
+ return;
}
Rect crop = image.getCropRect();
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 4496a82..e7da20b 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -1453,7 +1453,8 @@
/**
* The release date of this TV program.
*
- * <p>The value should be in the form of either "yyyy-MM-dd" or "yyyy".
+ * <p>The value should be in one of the following formats:
+ * "yyyy", "yyyy-MM-dd", and "yyyy-MM-ddTHH:mm:ssZ" (UTC in ISO 8601).
*
* <p>Type: TEXT
*/
@@ -1624,7 +1625,12 @@
/** Column definitions for the TV channels table. */
public static final class Channels implements BaseTvColumns {
- /** The content:// style URI for this table. */
+ /**
+ * The content:// style URI for this table.
+ *
+ * <p>SQL selection is not supported for {@link ContentResolver#query},
+ * {@link ContentResolver#update} and {@link ContentResolver#delete} operations.
+ */
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ PATH_CHANNEL);
@@ -2423,7 +2429,12 @@
*/
public static final class Programs implements BaseTvColumns, ProgramColumns {
- /** The content:// style URI for this table. */
+ /**
+ * The content:// style URI for this table.
+ *
+ * <p>SQL selection is not supported for {@link ContentResolver#query},
+ * {@link ContentResolver#update} and {@link ContentResolver#delete} operations.
+ */
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ PATH_PROGRAM);
@@ -2737,7 +2748,12 @@
*/
public static final class RecordedPrograms implements BaseTvColumns, ProgramColumns {
- /** The content:// style URI for this table. */
+ /**
+ * The content:// style URI for this table.
+ *
+ * <p>SQL selection is not supported for {@link ContentResolver#query},
+ * {@link ContentResolver#update} and {@link ContentResolver#delete} operations.
+ */
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ PATH_RECORDED_PROGRAM);
@@ -2858,7 +2874,12 @@
public static final class PreviewPrograms implements BaseTvColumns, ProgramColumns,
PreviewProgramColumns {
- /** The content:// style URI for this table. */
+ /**
+ * The content:// style URI for this table.
+ *
+ * <p>SQL selection is not supported for {@link ContentResolver#query},
+ * {@link ContentResolver#update} and {@link ContentResolver#delete} operations.
+ */
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ PATH_PREVIEW_PROGRAM);
@@ -2905,7 +2926,12 @@
public static final class WatchNextPrograms implements BaseTvColumns, ProgramColumns,
PreviewProgramColumns {
- /** The content:// style URI for this table. */
+ /**
+ * The content:// style URI for this table.
+ *
+ * <p>SQL selection is not supported for {@link ContentResolver#query},
+ * {@link ContentResolver#update} and {@link ContentResolver#delete} operations.
+ */
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ PATH_WATCH_NEXT_PROGRAM);
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index ed5fbcf..b5ea632 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -499,30 +499,23 @@
sp<Surface> surface = ctx->getProducer();
status_t res = OK;
- if (!isFormatOpaque(imageFormat)) {
- // TODO: need implement, see b/19962027
- jniThrowRuntimeException(env,
- "nativeAttachImage for non-opaque image is not implement yet!!!");
- return -1;
- }
-
- if (!isFormatOpaque(ctx->getBufferFormat())) {
+ if (isFormatOpaque(imageFormat) != isFormatOpaque(ctx->getBufferFormat())) {
jniThrowException(env, "java/lang/IllegalStateException",
- "Trying to attach an opaque image into a non-opaque ImageWriter");
+ "Trying to attach an opaque image into a non-opaque ImageWriter, or vice versa");
return -1;
}
// Image is guaranteed to be from ImageReader at this point, so it is safe to
// cast to BufferItem pointer.
- BufferItem* opaqueBuffer = reinterpret_cast<BufferItem*>(nativeBuffer);
- if (opaqueBuffer == NULL) {
+ BufferItem* buffer = reinterpret_cast<BufferItem*>(nativeBuffer);
+ if (buffer == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Image is not initialized or already closed");
return -1;
}
// Step 1. Attach Image
- res = surface->attachBuffer(opaqueBuffer->mGraphicBuffer.get());
+ res = surface->attachBuffer(buffer->mGraphicBuffer.get());
if (res != OK) {
ALOGE("Attach image failed: %s (%d)", strerror(-res), res);
switch (res) {
@@ -559,7 +552,7 @@
}
// Step 3. Queue Image.
- res = anw->queueBuffer(anw.get(), opaqueBuffer->mGraphicBuffer.get(), /*fenceFd*/
+ res = anw->queueBuffer(anw.get(), buffer->mGraphicBuffer.get(), /*fenceFd*/
-1);
if (res != OK) {
ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
@@ -817,4 +810,3 @@
return (ret1 || ret2);
}
-
diff --git a/packages/SettingsLib/res/layout/preference_two_target.xml b/packages/SettingsLib/res/layout/preference_two_target.xml
index 5446ace..9fb956e 100644
--- a/packages/SettingsLib/res/layout/preference_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_two_target.xml
@@ -37,7 +37,7 @@
android:id="@+id/icon_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minWidth="60dp"
+ android:minWidth="56dp"
android:orientation="horizontal"
android:paddingEnd="12dp"
android:paddingTop="4dp"
diff --git a/packages/SettingsLib/src/com/android/settingslib/SecureTouchListener.java b/packages/SettingsLib/src/com/android/settingslib/SecureTouchListener.java
new file mode 100644
index 0000000..766c42b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/SecureTouchListener.java
@@ -0,0 +1,44 @@
+package com.android.settingslib;
+
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.Toast;
+
+/**
+ * A touch listener which consumes touches when another window is partly or wholly obscuring the
+ * window containing the view this listener is attached to.
+ * Optionally accepts a string to show the user as a toast when consuming an insecure touch
+ */
+public class SecureTouchListener implements View.OnTouchListener {
+
+ private static final long TAP_DEBOUNCE_TIME = 2000;
+ private long mLastToastTime = 0;
+ private String mWarningText;
+
+ public SecureTouchListener() {
+ this(null);
+ }
+
+ public SecureTouchListener(String warningText) {
+ mWarningText = warningText;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0
+ || (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0) {
+ if (mWarningText != null) {
+ // Show a toast warning the user
+ final long currentTime = SystemClock.uptimeMillis();
+ if (currentTime - mLastToastTime > TAP_DEBOUNCE_TIME) {
+ mLastToastTime = currentTime;
+ Toast.makeText(v.getContext(), mWarningText, Toast.LENGTH_SHORT).show();
+ }
+ }
+ // Consume the touch event
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index ea28fe6..5a57e69 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -152,6 +152,12 @@
final MeasurementDetails details = new MeasurementDetails();
if (mVolume == null) return details;
+ if (mVolume.getType() == VolumeInfo.TYPE_PUBLIC) {
+ details.totalSize = mVolume.getPath().getTotalSpace();
+ details.availSize = mVolume.getPath().getUsableSpace();
+ return details;
+ }
+
try {
details.totalSize = mStats.getTotalBytes(mVolume.fsUuid);
details.availSize = mStats.getFreeBytes(mVolume.fsUuid);
@@ -161,7 +167,6 @@
return details;
}
-
final long finishTotal = SystemClock.elapsedRealtime();
Log.d(TAG, "Measured total storage in " + (finishTotal - start) + "ms");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4b304b2..14e2a85 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1142,28 +1142,55 @@
} finally {
Binder.restoreCallingIdentity(token);
}
+
+ final SettingsState ssaidSettings = mSettingsRegistry.getSettingsLocked(
+ SETTINGS_TYPE_SSAID, owningUserId);
+
if (instantSsaid != null) {
// Use the stored value if it is still valid.
if (ssaid != null && instantSsaid.equals(ssaid.getValue())) {
- return ssaid;
+ return mascaradeSsaidSetting(ssaidSettings, ssaid);
}
// The value has changed, update the stored value.
- final SettingsState ssaidSettings = mSettingsRegistry.getSettingsLocked(
- SETTINGS_TYPE_SSAID, owningUserId);
final boolean success = ssaidSettings.insertSettingLocked(name, instantSsaid, null,
true, callingPkg.packageName);
if (!success) {
throw new IllegalStateException("Failed to update instant app android id");
}
- return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID, owningUserId, name);
+ Setting setting = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID,
+ owningUserId, name);
+ return mascaradeSsaidSetting(ssaidSettings, setting);
}
// Lazy initialize ssaid if not yet present in ssaid table.
if (ssaid == null || ssaid.isNull() || ssaid.getValue() == null) {
- return mSettingsRegistry.generateSsaidLocked(callingPkg, owningUserId);
+ Setting setting = mSettingsRegistry.generateSsaidLocked(callingPkg, owningUserId);
+ return mascaradeSsaidSetting(ssaidSettings, setting);
}
- return ssaid;
+ return mascaradeSsaidSetting(ssaidSettings, ssaid);
+ }
+
+ private Setting mascaradeSsaidSetting(SettingsState settingsState, Setting ssaidSetting) {
+ // SSAID settings are located in a dedicated table for internal bookkeeping
+ // but for the world they reside in the secure table, so adjust the key here.
+ // We have a special name when looking it up but want the world to see it as
+ // "android_id".
+ if (ssaidSetting != null) {
+ return settingsState.new Setting(ssaidSetting) {
+ @Override
+ public int getKey() {
+ final int userId = getUserIdFromKey(super.getKey());
+ return makeKey(SETTINGS_TYPE_SECURE, userId);
+ }
+
+ @Override
+ public String getName() {
+ return Settings.Secure.ANDROID_ID;
+ }
+ };
+ }
+ return null;
}
private boolean insertSecureSetting(String name, String value, String tag,
@@ -1173,7 +1200,6 @@
+ ", " + tag + ", " + makeDefault + ", " + requestingUserId
+ ", " + forceNotify + ")");
}
-
return mutateSecureSetting(name, value, tag, makeDefault, requestingUserId,
MUTATION_OPERATION_INSERT, forceNotify, 0);
}
@@ -1879,6 +1905,7 @@
Bundle result = new Bundle();
result.putString(Settings.NameValueTable.VALUE,
!setting.isNull() ? setting.getValue() : null);
+
mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getKey());
return result;
}
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 5ee0c64..2fd7e87 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -31,6 +31,7 @@
LOCAL_STATIC_ANDROID_LIBRARIES := \
SystemUIPluginLib \
+ android-support-v4 \
android-support-v7-recyclerview \
android-support-v7-preference \
android-support-v7-appcompat \
diff --git a/packages/SystemUI/res/values/arrays_tv.xml b/packages/SystemUI/res/values/arrays_tv.xml
index e52c5db..7541b0e 100644
--- a/packages/SystemUI/res/values/arrays_tv.xml
+++ b/packages/SystemUI/res/values/arrays_tv.xml
@@ -31,5 +31,6 @@
<item>com.google.android.katniss.setting/.SpeechSettingsActivity</item>
<item>com.google.android.katniss.setting/.SearchSettingsActivity</item>
<item>com.google.android.gsf.notouch/.UsageDiagnosticsSettingActivity</item>
+ <item>com.google.android.tvlauncher/.notifications.NotificationsSidePanelActivity</item>
</string-array>
</resources>
diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml
index 40e3b12..ffd58dc 100644
--- a/packages/SystemUI/res/values/config_tv.xml
+++ b/packages/SystemUI/res/values/config_tv.xml
@@ -17,17 +17,9 @@
<resources>
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
when the PIP menu is shown with settings. -->
- <string translatable="false" name="pip_settings_bounds">"662 54 1142 324"</string>
+ <string translatable="false" name="pip_settings_bounds">"662 756 1142 1026"</string>
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
when the PIP menu is shown in center. -->
<string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
-
- <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
- when the PIP is shown in Recents without focus. -->
- <string translatable="false" name="pip_recents_bounds">"800 54 1120 234"</string>
-
- <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
- when the PIP is shown in Recents with focus. -->
- <string translatable="false" name="pip_recents_focused_bounds">"775 54 1145 262"</string>
</resources>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index e578068..a9bdb71 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -17,6 +17,14 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Picture-in-Picture (PIP) notification -->
+ <!-- Title for the notification channel for TV PIP controls. [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_tv_pip">Picture-in-Picture</string>
+ <!-- Title of the picture-in-picture (PIP) notification title
+ when the media doesn't have title [CHAR LIMIT=NONE] -->
+ <string name="pip_notification_unknown_title">(No title program)</string>
+
<!-- Picture-in-Picture (PIP) menu -->
<eat-comment />
<!-- Button to close picture-in-picture (PIP) in PIP menu [CHAR LIMIT=30] -->
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index d5cf1dd..5afa53f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -132,9 +132,6 @@
}
case MESSAGE_EXPAND_PIP: {
mListeners.forEach(l -> l.onPipExpand());
- // Preemptively mark the menu as invisible once we expand the PiP, but don't
- // resize as we will be animating the stack
- onMenuStateChanged(MENU_STATE_NONE, false /* resize */);
break;
}
case MESSAGE_MINIMIZE_PIP: {
@@ -143,9 +140,6 @@
}
case MESSAGE_DISMISS_PIP: {
mListeners.forEach(l -> l.onPipDismiss());
- // Preemptively mark the menu as invisible once we dismiss the PiP, but don't
- // resize as we'll be removing the stack in place
- onMenuStateChanged(MENU_STATE_NONE, false /* resize */);
break;
}
case MESSAGE_SHOW_MENU: {
@@ -308,6 +302,15 @@
}
/**
+ * Preemptively mark the menu as invisible, used when we are directly manipulating the pinned
+ * stack and don't want to trigger a resize which can animate the stack in a conflicting way
+ * (ie. when manually expanding or dismissing).
+ */
+ public void hideMenuWithoutResize() {
+ onMenuStateChanged(MENU_STATE_NONE, false /* resize */);
+ }
+
+ /**
* @return the current menu state.
*/
public int getMenuState() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 5121c8d..590e3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -77,6 +77,7 @@
private SurfaceFlingerVsyncChoreographer mVsyncChoreographer;
private Handler mHandler;
+ private PipMenuActivityController mMenuController;
private PipSnapAlgorithm mSnapAlgorithm;
private FlingAnimationUtils mFlingAnimationUtils;
@@ -93,10 +94,12 @@
};
public PipMotionHelper(Context context, IActivityManager activityManager,
- PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils) {
+ PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm,
+ FlingAnimationUtils flingAnimationUtils) {
mContext = context;
mHandler = BackgroundThread.getHandler();
mActivityManager = activityManager;
+ mMenuController = menuController;
mSnapAlgorithm = snapAlgorithm;
mFlingAnimationUtils = flingAnimationUtils;
mVsyncChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, mContext.getDisplay(),
@@ -148,6 +151,7 @@
*/
void expandPip(boolean skipAnimation) {
cancelAnimations();
+ mMenuController.hideMenuWithoutResize();
mHandler.post(() -> {
try {
if (skipAnimation) {
@@ -168,6 +172,7 @@
*/
void dismissPip() {
cancelAnimations();
+ mMenuController.hideMenuWithoutResize();
mHandler.post(() -> {
try {
mActivityManager.removeStack(PINNED_STACK_ID);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 9c7e3986..71d3d35 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -191,8 +191,8 @@
mGestures = new PipTouchGesture[] {
mDefaultMovementGesture
};
- mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mSnapAlgorithm,
- mFlingAnimationUtils);
+ mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mMenuController,
+ mSnapAlgorithm, mFlingAnimationUtils);
mExpandedShortestEdgeSize = context.getResources().getDimensionPixelSize(
R.dimen.pip_expanded_shortest_edge_size);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 9735bfc..6667b71 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -61,7 +61,8 @@
*/
public class PipManager implements BasePipManager {
private static final String TAG = "PipManager";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
private static final String SETTINGS_PACKAGE_AND_CLASS_DELIMITER = "/";
private static PipManager sPipManager;
@@ -122,6 +123,7 @@
private ComponentName mPipComponentName;
private MediaController mPipMediaController;
private String[] mLastPackagesResourceGranted;
+ private PipNotification mPipNotification;
private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
@@ -246,6 +248,8 @@
} catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
+
+ mPipNotification = new PipNotification(context);
}
private void loadConfigurationsAndApply() {
@@ -267,6 +271,7 @@
*/
public void onConfigurationChanged() {
loadConfigurationsAndApply();
+ mPipNotification.onConfigurationChanged(mContext);
}
/**
@@ -345,7 +350,7 @@
* @param state In Pip state also used to determine the new size for the Pip.
*/
void resizePinnedStack(int state) {
- if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state);
+ if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state, new Exception());
boolean wasStateNoPip = (mState == STATE_NO_PIP);
mResumeResizePinnedStackRunnable = state;
for (int i = mListeners.size() - 1; i >= 0; --i) {
@@ -511,8 +516,8 @@
/**
* Returns the PIPed activity's playback state.
- * This returns one of {@link PLAYBACK_STATE_PLAYING}, {@link PLAYBACK_STATE_PAUSED},
- * or {@link PLAYBACK_STATE_UNAVAILABLE}.
+ * This returns one of {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED},
+ * or {@link #PLAYBACK_STATE_UNAVAILABLE}.
*/
int getPlaybackState() {
if (mPipMediaController == null || mPipMediaController.getPlaybackState() == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
new file mode 100644
index 0000000..727eb5a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -0,0 +1,225 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.pip.tv;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.PlaybackState;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.util.NotificationChannels;
+import com.android.systemui.R;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+
+/**
+ * A notification that informs users that PIP is running and also provides PIP controls.
+ * <p>Once it's created, it will manage the PIP notification UI by itself except for handling
+ * configuration changes.
+ */
+public class PipNotification {
+ private static final String TAG = "PipNotification";
+ private static final boolean DEBUG = PipManager.DEBUG;
+
+ private static final String ACTION_MENU = "PipNotification.menu";
+ private static final String ACTION_CLOSE = "PipNotification.close";
+
+ private final PipManager mPipManager = PipManager.getInstance();
+
+ private final NotificationManager mNotificationManager;
+ private final Notification.Builder mNotificationBuilder;
+
+ private MediaController mMediaController;
+ private String mDefaultTitle;
+ private Icon mDefaultIcon;
+
+ private boolean mNotified;
+ private String mTitle;
+ private Bitmap mArt;
+
+ private PipManager.Listener mPipListener = new PipManager.Listener() {
+ @Override
+ public void onPipEntered() {
+ updateMediaControllerMetadata();
+ notifyPipNotification();
+ }
+
+ @Override
+ public void onPipActivityClosed() {
+ dismissPipNotification();
+ }
+
+ @Override
+ public void onShowPipMenu() {
+ // no-op.
+ }
+
+ @Override
+ public void onMoveToFullscreen() {
+ dismissPipNotification();
+ }
+
+ @Override
+ public void onPipResizeAboutToStart() {
+ // no-op.
+ }
+ };
+
+ private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ if (updateMediaControllerMetadata() && mNotified) {
+ // update notification
+ notifyPipNotification();
+ }
+ }
+ };
+
+ private final PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
+ @Override
+ public void onMediaControllerChanged() {
+ MediaController newController = mPipManager.getMediaController();
+ if (mMediaController == newController) {
+ return;
+ }
+ if (mMediaController != null) {
+ mMediaController.unregisterCallback(mMediaControllerCallback);
+ }
+ mMediaController = newController;
+ if (mMediaController != null) {
+ mMediaController.registerCallback(mMediaControllerCallback);
+ }
+ if (updateMediaControllerMetadata() && mNotified) {
+ // update notification
+ notifyPipNotification();
+ }
+ }
+ };
+
+ private final BroadcastReceiver mEventReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Log.d(TAG, "Received " + intent.getAction() + " from the notification UI");
+ }
+ switch (intent.getAction()) {
+ case ACTION_MENU:
+ mPipManager.showPictureInPictureMenu();
+ break;
+ case ACTION_CLOSE:
+ mPipManager.closePip();
+ break;
+ }
+ }
+ };
+
+ public PipNotification(Context context) {
+ mNotificationManager = (NotificationManager) context.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+
+ mNotificationBuilder = new Notification.Builder(context, NotificationChannels.TVPIP)
+ .setLocalOnly(true)
+ .setOngoing(false)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .extend(new Notification.TvExtender()
+ .setContentIntent(createPendingIntent(context, ACTION_MENU))
+ .setDeleteIntent(createPendingIntent(context, ACTION_CLOSE)));
+
+ mPipManager.addListener(mPipListener);
+ mPipManager.addMediaListener(mPipMediaListener);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_MENU);
+ intentFilter.addAction(ACTION_CLOSE);
+ context.registerReceiver(mEventReceiver, intentFilter);
+
+ onConfigurationChanged(context);
+ }
+
+ /**
+ * Called by {@link PipManager} when the configuration is changed.
+ */
+ void onConfigurationChanged(Context context) {
+ Resources res = context.getResources();
+ mDefaultTitle = res.getString(R.string.pip_notification_unknown_title);
+ mDefaultIcon = Icon.createWithResource(context,
+ res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR
+ ? R.drawable.pip_expand_ll : R.drawable.pip_expand_lr);
+ if (mNotified) {
+ // update notification
+ notifyPipNotification();
+ }
+ }
+
+ private void notifyPipNotification() {
+ mNotified = true;
+ mNotificationBuilder
+ .setShowWhen(true)
+ .setWhen(System.currentTimeMillis())
+ // TODO: Sending bitmap doesn't work in launcher side. Once launcher supports it,
+ // we can set icon.
+ //.setSmallIcon(mArt != null ? Icon.createWithBitmap(mArt) : mDefaultIcon)
+ .setSmallIcon(mDefaultIcon.getResId())
+ .setContentTitle(!TextUtils.isEmpty(mTitle) ? mTitle : mDefaultTitle);
+ mNotificationManager.notify(SystemMessage.NOTE_TV_PIP, mNotificationBuilder.build());
+ }
+
+ private void dismissPipNotification() {
+ mNotified = false;
+ mNotificationManager.cancel(SystemMessage.NOTE_TV_PIP);
+ }
+
+ private boolean updateMediaControllerMetadata() {
+ String title = null;
+ Bitmap art = null;
+ if (mPipManager.getMediaController() != null) {
+ MediaMetadata metadata = mPipManager.getMediaController().getMetadata();
+ if (metadata != null) {
+ title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE);
+ if (TextUtils.isEmpty(title)) {
+ title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
+ }
+ art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+ if (art == null) {
+ art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
+ }
+ }
+ }
+ if (!TextUtils.equals(title, mTitle) || art != mArt) {
+ mTitle = title;
+ mArt = art;
+ return true;
+ }
+ return false;
+ }
+
+ private static PendingIntent createPendingIntent(Context context, String action) {
+ return PendingIntent.getBroadcast(context, 0,
+ new Intent(action), PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 5512993..6f28838 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -68,6 +68,7 @@
private final Callback mCallback = new Callback();
private final ActivityStarter mActivityStarter;
private Dialog mDialog;
+ private boolean mRegistered;
public CastTile(QSHost host) {
super(host);
@@ -146,7 +147,7 @@
mDialog = dialog;
}
mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
- mDialog.show();
+ mUiHandler.post(() -> mDialog.show());
registerReceiver();
mHost.collapsePanels();
});
@@ -155,7 +156,13 @@
private void registerReceiver() {
mContext.registerReceiverAsUser(mReceiver, UserHandle.CURRENT,
new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), null, null);
- mDialog.setOnDismissListener(dialog -> mContext.unregisterReceiver(mReceiver));
+ mRegistered = true;
+ mDialog.setOnDismissListener(dialog -> {
+ if (mRegistered) {
+ mContext.unregisterReceiver(mReceiver);
+ mRegistered = false;
+ }
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index e34987b..4b614ed 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -362,11 +362,17 @@
* Cancels any current transform animations.
*/
public void cancelTransformAnimation() {
+ cancelDimAnimationIfExists();
Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
- Utilities.cancelAnimationWithoutCallbacks(mDimAnimator);
Utilities.cancelAnimationWithoutCallbacks(mOutlineAnimator);
}
+ private void cancelDimAnimationIfExists() {
+ if (mDimAnimator != null) {
+ mDimAnimator.cancel();
+ }
+ }
+
/** Enables/disables handling touch on this task view. */
public void setTouchEnabled(boolean enabled) {
setOnClickListener(enabled ? this : null);
@@ -546,7 +552,7 @@
@Override
public void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger) {
- Utilities.cancelAnimationWithoutCallbacks(mDimAnimator);
+ cancelDimAnimationIfExists();
// Dim the view after the app window transitions down into recents
postAnimationTrigger.increment();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index da56e62..90c65580 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -767,7 +767,7 @@
mDockedStackMinimized = minimized;
} else if (mDockedStackMinimized != minimized) {
mIsInMinimizeInteraction = true;
- if (minimized) {
+ if (minimized && (mCurrentAnimator == null || !mCurrentAnimator.isRunning())) {
mDividerPositionBeforeMinimized = getCurrentPosition();
}
mMinimizedSnapAlgorithm = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Abortable.java b/packages/SystemUI/src/com/android/systemui/statusbar/Abortable.java
new file mode 100644
index 0000000..d5ec4f67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Abortable.java
@@ -0,0 +1,24 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar;
+
+/**
+ * An interface that allows aborting existing operations.
+ */
+public interface Abortable {
+ void abort();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index d7eab97..b91561e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -891,7 +891,7 @@
* @return the calculated background color
*/
private int calculateBgColor(boolean withTint, boolean withOverRide) {
- if (mDark) {
+ if (withTint && mDark) {
return getContext().getColor(R.color.notification_material_background_dark_color);
}
if (withOverRide && mOverrideTint != NO_COLOR) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 8c1b334..9368747 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -355,7 +355,8 @@
NotificationColorUtil.getInstance(mContext));
int color = StatusBarIconView.NO_COLOR;
if (colorize) {
- color = mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded());
+ color = mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded(),
+ getBackgroundColorWithoutTint());
}
expandedIcon.setStaticDrawableColor(color);
}
@@ -859,7 +860,8 @@
private void updateNotificationColor() {
mNotificationColor = NotificationColorUtil.resolveContrastColor(mContext,
- getStatusBarNotification().getNotification().color);
+ getStatusBarNotification().getNotification().color,
+ getBackgroundColorWithoutTint());
}
public HybridNotificationView getSingleLineView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 540c391..f8bad05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -33,7 +33,6 @@
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.view.View;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -43,7 +42,6 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.statusbar.notification.InflationException;
-import com.android.systemui.statusbar.notification.NotificationInflater;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -86,7 +84,7 @@
public List<SnoozeCriterion> snoozeCriteria;
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
- private ArraySet<AsyncTask> mRunningTasks = new ArraySet();
+ private Abortable mRunningTask = null;
public Entry(StatusBarNotification n) {
this.key = n.getKey();
@@ -203,13 +201,15 @@
}
}
- public int getContrastedColor(Context context, boolean ambient) {
- int rawColor = ambient ? Notification.COLOR_DEFAULT :
+ public int getContrastedColor(Context context, boolean isLowPriority,
+ int backgroundColor) {
+ int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
notification.getNotification().color;
if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
return mCachedContrastColor;
}
- final int contrasted = NotificationColorUtil.resolveContrastColor(context, rawColor);
+ final int contrasted = NotificationColorUtil.resolveContrastColor(context, rawColor,
+ backgroundColor);
mCachedContrastColorIsFor = rawColor;
mCachedContrastColor = contrasted;
return mCachedContrastColor;
@@ -218,24 +218,26 @@
/**
* Abort all existing inflation tasks
*/
- public void abortInflation() {
- for (AsyncTask task : mRunningTasks) {
- task.cancel(true /* mayInterruptIfRunning */);
+ public void abortTask() {
+ if (mRunningTask != null) {
+ mRunningTask.abort();
+ mRunningTask = null;
}
- mRunningTasks.clear();
}
- public void addInflationTask(AsyncTask asyncInflationTask) {
- mRunningTasks.add(asyncInflationTask);
+ public void setInflationTask(Abortable abortableTask) {
+ // abort any existing inflation
+ abortTask();
+ mRunningTask = abortableTask;
}
- public void onInflationTaskFinished(AsyncTask asyncInflationTask) {
- mRunningTasks.remove(asyncInflationTask);
+ public void onInflationTaskFinished() {
+ mRunningTask = null;
}
@VisibleForTesting
- public ArraySet<AsyncTask> getRunningTasks() {
- return mRunningTasks;
+ public Abortable getRunningTask() {
+ return mRunningTask;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 1375310..23a67e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -95,14 +95,25 @@
/**
* Called when the guts view have been told to close, typically after an outside
- * interaction. Returning {@code true} here will prevent the guts view to close.
+ * interaction.
+ *
+ * @param save whether the state should be saved.
+ * @param force whether the guts view should be forced closed regardless of state.
+ * @return if closing the view has been handled.
*/
- public boolean handleCloseControls(boolean save);
+ public boolean handleCloseControls(boolean save, boolean force);
/**
* @return whether the notification associated with these guts is set to be removed.
*/
public boolean willBeRemoved();
+
+ /**
+ * @return whether these guts are a leavebehind (e.g. {@link NotificationSnooze}).
+ */
+ public default boolean isLeavebehind() {
+ return false;
+ }
}
public interface OnGutsClosedListener {
@@ -125,7 +136,7 @@
@Override
public void run() {
if (mNeedsFalsingProtection && mExposed) {
- closeControls(-1 /* x */, -1 /* y */, false /* save */);
+ closeControls(-1 /* x */, -1 /* y */, false /* save */, false /* force */);
}
}
};
@@ -144,6 +155,10 @@
addView(mGutsContent.getContentView());
}
+ public GutsContent getGutsContent() {
+ return mGutsContent;
+ }
+
public void resetFalsingCheck() {
mHandler.removeCallbacks(mFalsingCheck);
if (mNeedsFalsingProtection && mExposed) {
@@ -197,19 +212,30 @@
}
}
- public void closeControls(int x, int y, boolean save) {
+ public void closeControls(boolean leavebehinds, boolean controls, int x, int y, boolean force) {
+ if (mGutsContent != null) {
+ if (mGutsContent.isLeavebehind() && leavebehinds) {
+ closeControls(x, y, true /* save */, force);
+ } else if (!mGutsContent.isLeavebehind() && controls) {
+ closeControls(x, y, true /* save */, force);
+ }
+ }
+ }
+
+ public void closeControls(int x, int y, boolean save, boolean force) {
if (getWindowToken() == null) {
if (mClosedListener != null) {
mClosedListener.onGutsClosed(this);
}
return;
}
- if (mGutsContent == null || !mGutsContent.handleCloseControls(save)) {
+
+ if (mGutsContent == null || !mGutsContent.handleCloseControls(save, force)) {
animateClose(x, y);
- }
- setExposed(false, mNeedsFalsingProtection);
- if (mClosedListener != null) {
- mClosedListener.onGutsClosed(this);
+ setExposed(false, mNeedsFalsingProtection);
+ if (mClosedListener != null) {
+ mClosedListener.onGutsClosed(this);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 2cf06c2..0c2c5bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -393,7 +393,7 @@
}
@Override
- public boolean handleCloseControls(boolean save) {
+ public boolean handleCloseControls(boolean save, boolean force) {
if (save && hasImportanceChanged()) {
if (mCheckSaveListener != null) {
mCheckSaveListener.checkSave(() -> { saveImportance(); });
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
index 4305bdef..dc538da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
@@ -35,6 +35,7 @@
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.service.notification.StatusBarNotification;
import android.view.LayoutInflater;
@@ -107,7 +108,7 @@
mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
mIconPadding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding);
- mHandler = new Handler();
+ mHandler = new Handler(Looper.getMainLooper());
mMenuItems = new ArrayList<>();
mSnoozeItem = createSnoozeItem(context);
mInfoItem = createInfoItem(context);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
index 8b3d6d9..8830c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
@@ -103,6 +103,18 @@
createOptionViews();
}
+ public boolean isExpanded() {
+ return mExpanded;
+ }
+
+ public void setSnoozeListener(NotificationSwipeActionHelper listener) {
+ mSnoozeListener = listener;
+ }
+
+ public void setStatusBarNotification(StatusBarNotification sbn) {
+ mSbn = sbn;
+ }
+
private ArrayList<SnoozeOption> getDefaultSnoozeOptions() {
ArrayList<SnoozeOption> options = new ArrayList<>();
options.add(createOption(R.string.snooze_option_15_min, 15));
@@ -203,7 +215,7 @@
final int x = targetLoc[0] - parentLoc[0] + centerX;
final int y = targetLoc[1] - parentLoc[1] + centerY;
showSnoozeOptions(false);
- mGutsContainer.closeControls(x, y, false /* save */);
+ mGutsContainer.closeControls(x, y, false /* save */, false /* force */);
}
}
@@ -224,29 +236,31 @@
return this;
}
- public void setStatusBarNotification(StatusBarNotification sbn) {
- mSbn = sbn;
- }
-
@Override
public void setGutsParent(NotificationGuts guts) {
mGutsContainer = guts;
}
- public void setSnoozeListener(NotificationSwipeActionHelper listener) {
- mSnoozeListener = listener;
- }
-
@Override
- public boolean handleCloseControls(boolean save) {
- // When snooze is closed (i.e. there was interaction outside of the notification)
- // then we commit the snooze action.
- if (mSnoozeListener != null && mSelectedOption != null) {
+ public boolean handleCloseControls(boolean save, boolean force) {
+ if (mExpanded && !force) {
+ // Collapse expanded state on outside touch
+ showSnoozeOptions(false);
+ return true;
+ } else if (mSnoozeListener != null && mSelectedOption != null) {
+ // Snooze option selected so commit it
mSnoozing = true;
mSnoozeListener.snooze(mSbn, mSelectedOption);
return true;
+ } else {
+ // The view should actually be closed
+ setSelected(mSnoozeOptions.get(0));
+ return false; // Return false here so that guts handles closing the view
}
- // The view should be closed
- return false;
+ }
+
+ @Override
+ public boolean isLeavebehind() {
+ return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
index 7cfc767..f1c26cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
@@ -16,19 +16,26 @@
package com.android.systemui.statusbar.notification;
+import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.os.AsyncTask;
+import android.os.CancellationSignal;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.Abortable;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationContentView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.Assert;
+
+import java.util.HashMap;
/**
* A utility that inflates the right kind of contentView based on the state
@@ -116,126 +123,303 @@
@VisibleForTesting
void inflateNotificationViews(int reInflateFlags) {
StatusBarNotification sbn = mRow.getEntry().notification;
- new AsyncInflationTask(mRow.getContext(), sbn, reInflateFlags).execute();
+ new AsyncInflationTask(sbn, reInflateFlags, mRow, mIsLowPriority,
+ mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
+ mCallback, mRemoteViewClickHandler).execute();
}
@VisibleForTesting
- void inflateNotificationViews(int reInflateFlags,
+ InflationProgress inflateNotificationViews(int reInflateFlags,
Notification.Builder builder, Context packageContext) {
- NotificationData.Entry entry = mRow.getEntry();
- NotificationContentView privateLayout = mRow.getPrivateLayout();
- NotificationContentView publicLayout = mRow.getPublicLayout();
+ InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority,
+ mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
+ mRedactAmbient, packageContext);
+ apply(result, reInflateFlags, mRow, mRedactAmbient, mRemoteViewClickHandler, null);
+ return result;
+ }
- boolean isLowPriority = mIsLowPriority && !mIsChildInGroup;
+ private static InflationProgress createRemoteViews(int reInflateFlags,
+ Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
+ boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
+ Context packageContext) {
+ InflationProgress result = new InflationProgress();
+ isLowPriority = isLowPriority && !isChildInGroup;
if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
- final RemoteViews newContentView = createContentView(builder,
- isLowPriority, mUsesIncreasedHeight);
- if (!compareRemoteViews(newContentView,
- entry.cachedContentView)) {
- View contentViewLocal = newContentView.apply(
- packageContext,
- privateLayout,
- mRemoteViewClickHandler);
- contentViewLocal.setIsRootNamespace(true);
- privateLayout.setContractedChild(contentViewLocal);
- } else {
- newContentView.reapply(packageContext,
- privateLayout.getContractedChild(),
- mRemoteViewClickHandler);
- }
- entry.cachedContentView = newContentView;
+ result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
}
if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
- final RemoteViews newBigContentView = createBigContentView(
- builder, isLowPriority);
- if (newBigContentView != null) {
- if (!compareRemoteViews(newBigContentView, entry.cachedBigContentView)) {
- View bigContentViewLocal = newBigContentView.apply(
- packageContext,
- privateLayout,
- mRemoteViewClickHandler);
- bigContentViewLocal.setIsRootNamespace(true);
- privateLayout.setExpandedChild(bigContentViewLocal);
- } else {
- newBigContentView.reapply(packageContext,
- privateLayout.getExpandedChild(),
- mRemoteViewClickHandler);
- }
- } else if (entry.cachedBigContentView != null) {
- privateLayout.setExpandedChild(null);
- }
- entry.cachedBigContentView = newBigContentView;
- mRow.setExpandable(newBigContentView != null);
+ result.newExpandedView = createExpandedView(builder, isLowPriority);
}
if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
- final RemoteViews newHeadsUpContentView =
- builder.createHeadsUpContentView(mUsesIncreasedHeadsUpHeight);
- if (newHeadsUpContentView != null) {
- if (!compareRemoteViews(newHeadsUpContentView,
- entry.cachedHeadsUpContentView)) {
- View headsUpContentViewLocal = newHeadsUpContentView.apply(
- packageContext,
- privateLayout,
- mRemoteViewClickHandler);
- headsUpContentViewLocal.setIsRootNamespace(true);
- privateLayout.setHeadsUpChild(headsUpContentViewLocal);
- } else {
- newHeadsUpContentView.reapply(packageContext,
- privateLayout.getHeadsUpChild(),
- mRemoteViewClickHandler);
- }
- } else if (entry.cachedHeadsUpContentView != null) {
- privateLayout.setHeadsUpChild(null);
- }
- entry.cachedHeadsUpContentView = newHeadsUpContentView;
+ result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
}
if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
- final RemoteViews newPublicNotification
- = builder.makePublicContentView();
- if (!compareRemoteViews(newPublicNotification, entry.cachedPublicContentView)) {
- View publicContentView = newPublicNotification.apply(
- packageContext,
- publicLayout,
- mRemoteViewClickHandler);
- publicContentView.setIsRootNamespace(true);
- publicLayout.setContractedChild(publicContentView);
- } else {
- newPublicNotification.reapply(packageContext,
- publicLayout.getContractedChild(),
- mRemoteViewClickHandler);
- }
- entry.cachedPublicContentView = newPublicNotification;
+ result.newPublicView = builder.makePublicContentView();
}
if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
- final RemoteViews newAmbientNotification = mRedactAmbient
- ? builder.makePublicAmbientNotification()
+ result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
: builder.makeAmbientNotification();
- NotificationContentView newParent = mRedactAmbient ? publicLayout : privateLayout;
- NotificationContentView otherParent = !mRedactAmbient ? publicLayout : privateLayout;
+ }
+ result.packageContext = packageContext;
+ return result;
+ }
- if (newParent.getAmbientChild() == null ||
- !compareRemoteViews(newAmbientNotification, entry.cachedAmbientContentView)) {
- View ambientContentView = newAmbientNotification.apply(
- packageContext,
- newParent,
- mRemoteViewClickHandler);
- ambientContentView.setIsRootNamespace(true);
- newParent.setAmbientChild(ambientContentView);
- otherParent.setAmbientChild(null);
- } else {
- newAmbientNotification.reapply(packageContext,
- newParent.getAmbientChild(),
- mRemoteViewClickHandler);
+ public static CancellationSignal apply(InflationProgress result, int reInflateFlags,
+ ExpandableNotificationRow row, boolean redactAmbient,
+ RemoteViews.OnClickHandler remoteViewClickHandler,
+ @Nullable InflationCallback callback) {
+ NotificationData.Entry entry = row.getEntry();
+ NotificationContentView privateLayout = row.getPrivateLayout();
+ NotificationContentView publicLayout = row.getPublicLayout();
+ final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>();
+
+ int flag = FLAG_REINFLATE_CONTENT_VIEW;
+ if ((reInflateFlags & flag) != 0) {
+ boolean isNewView = !compareRemoteViews(result.newContentView, entry.cachedContentView);
+ ApplyCallback applyCallback = new ApplyCallback() {
+ @Override
+ public void setResultView(View v) {
+ result.inflatedContentView = v;
+ }
+
+ @Override
+ public RemoteViews getRemoteView() {
+ return result.newContentView;
+ }
+ };
+ applyRemoteView(result, reInflateFlags, flag, row, redactAmbient,
+ isNewView, remoteViewClickHandler, callback, entry, privateLayout,
+ privateLayout.getContractedChild(),
+ runningInflations, applyCallback);
+ }
+
+ flag = FLAG_REINFLATE_EXPANDED_VIEW;
+ if ((reInflateFlags & flag) != 0) {
+ if (result.newExpandedView != null) {
+ boolean isNewView = !compareRemoteViews(result.newExpandedView,
+ entry.cachedBigContentView);
+ ApplyCallback applyCallback = new ApplyCallback() {
+ @Override
+ public void setResultView(View v) {
+ result.inflatedExpandedView = v;
+ }
+
+ @Override
+ public RemoteViews getRemoteView() {
+ return result.newExpandedView;
+ }
+ };
+ applyRemoteView(result, reInflateFlags, flag, row,
+ redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ privateLayout, privateLayout.getExpandedChild(), runningInflations,
+ applyCallback);
}
- entry.cachedAmbientContentView = newAmbientNotification;
+ }
+
+ flag = FLAG_REINFLATE_HEADS_UP_VIEW;
+ if ((reInflateFlags & flag) != 0) {
+ if (result.newHeadsUpView != null) {
+ boolean isNewView = !compareRemoteViews(result.newHeadsUpView,
+ entry.cachedHeadsUpContentView);
+ ApplyCallback applyCallback = new ApplyCallback() {
+ @Override
+ public void setResultView(View v) {
+ result.inflatedHeadsUpView = v;
+ }
+
+ @Override
+ public RemoteViews getRemoteView() {
+ return result.newHeadsUpView;
+ }
+ };
+ applyRemoteView(result, reInflateFlags, flag, row,
+ redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ privateLayout, privateLayout.getHeadsUpChild(), runningInflations,
+ applyCallback);
+ }
+ }
+
+ flag = FLAG_REINFLATE_PUBLIC_VIEW;
+ if ((reInflateFlags & flag) != 0) {
+ boolean isNewView = !compareRemoteViews(result.newPublicView,
+ entry.cachedPublicContentView);
+ ApplyCallback applyCallback = new ApplyCallback() {
+ @Override
+ public void setResultView(View v) {
+ result.inflatedPublicView = v;
+ }
+
+ @Override
+ public RemoteViews getRemoteView() {
+ return result.newPublicView;
+ }
+ };
+ applyRemoteView(result, reInflateFlags, flag, row,
+ redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ publicLayout, publicLayout.getContractedChild(), runningInflations,
+ applyCallback);
+ }
+
+ flag = FLAG_REINFLATE_AMBIENT_VIEW;
+ if ((reInflateFlags & flag) != 0) {
+ NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout;
+ boolean isNewView = !canReapplyAmbient(row, redactAmbient) ||
+ !compareRemoteViews(result.newAmbientView, entry.cachedAmbientContentView);
+ ApplyCallback applyCallback = new ApplyCallback() {
+ @Override
+ public void setResultView(View v) {
+ result.inflatedAmbientView = v;
+ }
+
+ @Override
+ public RemoteViews getRemoteView() {
+ return result.newAmbientView;
+ }
+ };
+ applyRemoteView(result, reInflateFlags, flag, row,
+ redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ newParent, newParent.getAmbientChild(), runningInflations,
+ applyCallback);
+ }
+
+ // Let's try to finish, maybe nobody is even inflating anything
+ finishIfDone(result, reInflateFlags, runningInflations, callback, row,
+ redactAmbient);
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ cancellationSignal.setOnCancelListener(
+ () -> runningInflations.values().forEach(CancellationSignal::cancel));
+ return cancellationSignal;
+ }
+
+ private static void applyRemoteView(final InflationProgress result,
+ final int reInflateFlags, int inflationId,
+ final ExpandableNotificationRow row,
+ final boolean redactAmbient, boolean isNewView,
+ RemoteViews.OnClickHandler remoteViewClickHandler,
+ @Nullable final InflationCallback callback, NotificationData.Entry entry,
+ NotificationContentView parentLayout, View existingView,
+ final HashMap<Integer, CancellationSignal> runningInflations,
+ ApplyCallback applyCallback) {
+ RemoteViews.OnViewAppliedListener listener
+ = new RemoteViews.OnViewAppliedListener() {
+
+ @Override
+ public void onViewApplied(View v) {
+ if (isNewView) {
+ v.setIsRootNamespace(true);
+ applyCallback.setResultView(v);
+ }
+ runningInflations.remove(inflationId);
+ finishIfDone(result, reInflateFlags, runningInflations, callback, row,
+ redactAmbient);
+ }
+
+ @Override
+ public void onError(Exception e) {
+ runningInflations.remove(inflationId);
+ handleInflationError(runningInflations, e, entry.notification, callback);
+ }
+ };
+ CancellationSignal cancellationSignal;
+ RemoteViews newContentView = applyCallback.getRemoteView();
+ if (isNewView) {
+ cancellationSignal = newContentView.applyAsync(
+ result.packageContext,
+ parentLayout,
+ null /* executor */,
+ listener,
+ remoteViewClickHandler);
+ } else {
+ cancellationSignal = newContentView.reapplyAsync(
+ result.packageContext,
+ existingView,
+ null /* executor */,
+ listener,
+ remoteViewClickHandler);
+ }
+ runningInflations.put(inflationId, cancellationSignal);
+ }
+
+ private static void handleInflationError(HashMap<Integer, CancellationSignal> runningInflations,
+ Exception e, StatusBarNotification notification, @Nullable InflationCallback callback) {
+ Assert.isMainThread();
+ runningInflations.values().forEach(CancellationSignal::cancel);
+ if (callback != null) {
+ callback.handleInflationException(notification, e);
}
}
- private RemoteViews createBigContentView(Notification.Builder builder,
+ /**
+ * Finish the inflation of the views
+ *
+ * @return true if the inflation was finished
+ */
+ private static boolean finishIfDone(InflationProgress result, int reInflateFlags,
+ HashMap<Integer, CancellationSignal> runningInflations,
+ @Nullable InflationCallback endListener, ExpandableNotificationRow row,
+ boolean redactAmbient) {
+ Assert.isMainThread();
+ NotificationData.Entry entry = row.getEntry();
+ NotificationContentView privateLayout = row.getPrivateLayout();
+ NotificationContentView publicLayout = row.getPublicLayout();
+ if (runningInflations.isEmpty()) {
+ if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
+ if (result.inflatedContentView != null) {
+ privateLayout.setContractedChild(result.inflatedContentView);
+ }
+ entry.cachedContentView = result.newContentView;
+ }
+
+ if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
+ if (result.inflatedExpandedView != null) {
+ privateLayout.setExpandedChild(result.inflatedExpandedView);
+ } else if (result.newExpandedView == null) {
+ privateLayout.setExpandedChild(null);
+ }
+ entry.cachedBigContentView = result.newExpandedView;
+ row.setExpandable(result.newExpandedView != null);
+ }
+
+ if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
+ if (result.inflatedHeadsUpView != null) {
+ privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
+ } else if (result.newHeadsUpView == null) {
+ privateLayout.setHeadsUpChild(null);
+ }
+ entry.cachedHeadsUpContentView = result.newHeadsUpView;
+ }
+
+ if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
+ if (result.inflatedPublicView != null) {
+ publicLayout.setContractedChild(result.inflatedPublicView);
+ }
+ entry.cachedPublicContentView = result.newPublicView;
+ }
+
+ if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
+ if (result.inflatedAmbientView != null) {
+ NotificationContentView newParent = redactAmbient
+ ? publicLayout : privateLayout;
+ NotificationContentView otherParent = !redactAmbient
+ ? publicLayout : privateLayout;
+ newParent.setAmbientChild(result.inflatedAmbientView);
+ otherParent.setAmbientChild(null);
+ }
+ entry.cachedAmbientContentView = result.newAmbientView;
+ }
+ if (endListener != null) {
+ endListener.onAsyncInflationFinished(row.getEntry());
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private static RemoteViews createExpandedView(Notification.Builder builder,
boolean isLowPriority) {
RemoteViews bigContentView = builder.createBigContentView();
if (bigContentView != null) {
@@ -249,7 +433,7 @@
return null;
}
- private RemoteViews createContentView(Notification.Builder builder,
+ private static RemoteViews createContentView(Notification.Builder builder,
boolean isLowPriority, boolean useLarge) {
if (isLowPriority) {
return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
@@ -258,7 +442,7 @@
}
// Returns true if the RemoteViews are the same.
- private boolean compareRemoteViews(final RemoteViews a, final RemoteViews b) {
+ private static boolean compareRemoteViews(final RemoteViews a, final RemoteViews b) {
return (a == null && b == null) ||
(a != null && b != null
&& b.getPackage() != null
@@ -272,7 +456,7 @@
}
public interface InflationCallback {
- void handleInflationException(StatusBarNotification notification, InflationException e);
+ void handleInflationException(StatusBarNotification notification, Exception e);
void onAsyncInflationFinished(NotificationData.Entry entry);
}
@@ -286,37 +470,73 @@
inflateNotificationViews();
}
- private class AsyncInflationTask extends AsyncTask<Void, Void, Notification.Builder> {
+ private static boolean canReapplyAmbient(ExpandableNotificationRow row, boolean redactAmbient) {
+ NotificationContentView ambientView = redactAmbient ? row.getPublicLayout()
+ : row.getPrivateLayout(); ;
+ return ambientView.getAmbientChild() != null;
+ }
+
+ public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
+ implements InflationCallback, Abortable {
private final StatusBarNotification mSbn;
private final Context mContext;
private final int mReInflateFlags;
- private Context mPackageContext = null;
+ private final boolean mIsLowPriority;
+ private final boolean mIsChildInGroup;
+ private final boolean mUsesIncreasedHeight;
+ private final InflationCallback mCallback;
+ private final boolean mUsesIncreasedHeadsUpHeight;
+ private final boolean mRedactAmbient;
+ private ExpandableNotificationRow mRow;
private Exception mError;
+ private RemoteViews.OnClickHandler mRemoteViewClickHandler;
+ private CancellationSignal mCancellationSignal;
- private AsyncInflationTask(Context context, StatusBarNotification notification,
- int reInflateFlags) {
+ private AsyncInflationTask(StatusBarNotification notification,
+ int reInflateFlags, ExpandableNotificationRow row, boolean isLowPriority,
+ boolean isChildInGroup, boolean usesIncreasedHeight,
+ boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
+ InflationCallback callback,
+ RemoteViews.OnClickHandler remoteViewClickHandler) {
+ mRow = row;
+ NotificationData.Entry entry = row.getEntry();
+ entry.setInflationTask(this);
mSbn = notification;
- mContext = context;
mReInflateFlags = reInflateFlags;
- mRow.getEntry().addInflationTask(this);
+ mContext = mRow.getContext();
+ mIsLowPriority = isLowPriority;
+ mIsChildInGroup = isChildInGroup;
+ mUsesIncreasedHeight = usesIncreasedHeight;
+ mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
+ mRedactAmbient = redactAmbient;
+ mRemoteViewClickHandler = remoteViewClickHandler;
+ mCallback = callback;
}
@Override
- protected Notification.Builder doInBackground(Void... params) {
+ protected InflationProgress doInBackground(Void... params) {
try {
final Notification.Builder recoveredBuilder
= Notification.Builder.recoverBuilder(mContext,
mSbn.getNotification());
- mPackageContext = mSbn.getPackageContext(mContext);
+ Context packageContext = mSbn.getPackageContext(mContext);
Notification notification = mSbn.getNotification();
+ if (mIsLowPriority) {
+ int backgroundColor = mContext.getColor(
+ R.color.notification_material_background_low_priority_color);
+ recoveredBuilder.setBackgroundColorHint(backgroundColor);
+ }
if (notification.isMediaNotification()) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
- mPackageContext);
+ packageContext);
processor.setIsLowPriority(mIsLowPriority);
processor.processNotification(notification, recoveredBuilder);
}
- return recoveredBuilder;
+ return createRemoteViews(mReInflateFlags,
+ recoveredBuilder, mIsLowPriority, mIsChildInGroup,
+ mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
+ packageContext);
} catch (Exception e) {
mError = e;
return null;
@@ -324,34 +544,64 @@
}
@Override
- protected void onPostExecute(Notification.Builder builder) {
- mRow.getEntry().onInflationTaskFinished(this);
+ protected void onPostExecute(InflationProgress result) {
if (mError == null) {
- finishInflation(mReInflateFlags, builder, mPackageContext);
+ mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient,
+ mRemoteViewClickHandler, this);
} else {
handleError(mError);
}
}
- }
- private void finishInflation(int reinflationFlags, Notification.Builder builder,
- Context context) {
- try {
- inflateNotificationViews(reinflationFlags, builder, context);
- } catch (RuntimeException e){
- handleError(e);
- return;
+ private void handleError(Exception e) {
+ mRow.getEntry().onInflationTaskFinished();
+ StatusBarNotification sbn = mRow.getStatusBarNotification();
+ final String ident = sbn.getPackageName() + "/0x"
+ + Integer.toHexString(sbn.getId());
+ Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
+ mCallback.handleInflationException(sbn,
+ new InflationException("Couldn't inflate contentViews" + e));
}
- mRow.onNotificationUpdated();
- mCallback.onAsyncInflationFinished(mRow.getEntry());
+
+ @Override
+ public void abort() {
+ cancel(true /* mayInterruptIfRunning */);
+ if (mCancellationSignal != null) {
+ mCancellationSignal.cancel();
+ }
+ }
+
+ @Override
+ public void handleInflationException(StatusBarNotification notification, Exception e) {
+ handleError(e);
+ }
+
+ @Override
+ public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ mRow.getEntry().onInflationTaskFinished();
+ mRow.onNotificationUpdated();
+ mCallback.onAsyncInflationFinished(mRow.getEntry());
+ }
}
- private void handleError(Exception e) {
- StatusBarNotification sbn = mRow.getStatusBarNotification();
- final String ident = sbn.getPackageName() + "/0x"
- + Integer.toHexString(sbn.getId());
- Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
- mCallback.handleInflationException(sbn,
- new InflationException("Couldn't inflate contentViews" + e));
+ private static class InflationProgress {
+ private RemoteViews newContentView;
+ private RemoteViews newHeadsUpView;
+ private RemoteViews newExpandedView;
+ private RemoteViews newAmbientView;
+ private RemoteViews newPublicView;
+
+ private Context packageContext;
+
+ private View inflatedContentView;
+ private View inflatedHeadsUpView;
+ private View inflatedExpandedView;
+ private View inflatedAmbientView;
+ private View inflatedPublicView;
+ }
+
+ private abstract static class ApplyCallback {
+ public abstract void setResultView(View v);
+ public abstract RemoteViews getRemoteView();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
new file mode 100644
index 0000000..1bfc0cc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
@@ -0,0 +1,65 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.content.Context;
+import android.support.v4.view.AsyncLayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.Abortable;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
+
+/**
+ * An inflater task that asynchronously inflates a ExpandableNotificationRow
+ */
+public class RowInflaterTask implements Abortable, AsyncLayoutInflater.OnInflateFinishedListener {
+ private RowInflationFinishedListener mListener;
+ private NotificationData.Entry mEntry;
+ private boolean mCancelled;
+
+ /**
+ * Inflates a new notificationView. This should not be called twice on this object
+ */
+ public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
+ RowInflationFinishedListener listener) {
+ mListener = listener;
+ AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
+ mEntry = entry;
+ entry.setInflationTask(this);
+ inflater.inflate(R.layout.status_bar_notification_row, parent, this);
+ }
+
+ @Override
+ public void abort() {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onInflateFinished(View view, int resid, ViewGroup parent) {
+ if (!mCancelled) {
+ mEntry.onInflationTaskFinished();
+ mListener.onInflationFinished((ExpandableNotificationRow) view);
+ }
+ }
+
+ public interface RowInflationFinishedListener {
+ void onInflationFinished(ExpandableNotificationRow row);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 1a9a40b..674a61c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -168,7 +168,8 @@
distance = mTranslationOnDown + distance;
distance = Math.max(0, distance);
}
- setTranslation(distance, false /* isReset */, false /* animateReset */);
+ setTranslation(distance, false /* isReset */, false /* animateReset */,
+ false /* force */);
}
break;
@@ -373,11 +374,12 @@
targetView.finishAnimation(velocity, mAnimationEndRunnable);
}
- private void setTranslation(float translation, boolean isReset, boolean animateReset) {
+ private void setTranslation(float translation, boolean isReset, boolean animateReset,
+ boolean force) {
translation = rightSwipePossible() ? translation : Math.max(0, translation);
translation = leftSwipePossible() ? translation : Math.min(0, translation);
float absTranslation = Math.abs(translation);
- if (translation != mTranslation || isReset) {
+ if (translation != mTranslation || isReset || force) {
KeyguardAffordanceView targetView = translation > 0 ? mLeftIcon : mRightIcon;
KeyguardAffordanceView otherView = translation > 0 ? mRightIcon : mLeftIcon;
float alpha = absTranslation / getMinTranslationAmount();
@@ -392,15 +394,15 @@
boolean slowAnimation = isReset && isBelowFalsingThreshold();
if (!isReset) {
updateIcon(targetView, radius, alpha + fadeOutAlpha * targetView.getRestingAlpha(),
- false, false, false, false);
+ false, false, force, false);
} else {
updateIcon(targetView, 0.0f, fadeOutAlpha * targetView.getRestingAlpha(),
- animateIcons, slowAnimation, false, forceNoCircleAnimation);
+ animateIcons, slowAnimation, force, forceNoCircleAnimation);
}
updateIcon(otherView, 0.0f, fadeOutAlpha * otherView.getRestingAlpha(),
- animateIcons, slowAnimation, false, forceNoCircleAnimation);
+ animateIcons, slowAnimation, force, forceNoCircleAnimation);
updateIcon(mCenterIcon, 0.0f, fadeOutAlpha * mCenterIcon.getRestingAlpha(),
- animateIcons, slowAnimation, false, forceNoCircleAnimation);
+ animateIcons, slowAnimation, force, forceNoCircleAnimation);
mTranslation = translation;
}
@@ -508,8 +510,12 @@
}
public void reset(boolean animate) {
+ reset(animate, false /* force */);
+ }
+
+ public void reset(boolean animate, boolean force) {
cancelAnimation();
- setTranslation(0.0f, true, animate);
+ setTranslation(0.0f, true, animate, force);
mMotionCancelled = true;
if (mSwipingInProgress) {
mCallback.onSwipingAborted();
@@ -517,6 +523,10 @@
}
}
+ public void resetImmediately() {
+ reset(false /* animate */, true /* force */);
+ }
+
public boolean isSwipingInProgress() {
return mSwipingInProgress;
}
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 8a97be5..f7480bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -46,7 +46,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.DejankUtils;
-import com.android.systemui.EventLogTags;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
@@ -524,7 +523,8 @@
mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
}
closeQs();
- mStatusBar.dismissPopups();
+ mStatusBar.closeAndSaveGuts(true /* leavebehind */, true /* force */,
+ true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
true /* cancelAnimators */);
mNotificationStackScroller.resetScrollPosition();
@@ -1015,6 +1015,7 @@
float height = mQsExpansionHeight - overscrollAmount;
setQsExpansion(height);
requestPanelHeightUpdate();
+ mNotificationStackScroller.checkSnoozeLeavebehind();
}
private void setQsExpanded(boolean expanded) {
@@ -1381,6 +1382,7 @@
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
+ mNotificationStackScroller.resetCheckSnoozeLeavebehind();
mQsExpansionAnimator = null;
if (onFinishRunnable != null) {
onFinishRunnable.run();
@@ -2463,6 +2465,14 @@
}
};
+ @Override
+ public void setTouchDisabled(boolean disabled) {
+ super.setTouchDisabled(disabled);
+ if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
+ mAffordanceHelper.resetImmediately();
+ }
+ }
+
public void setDark(boolean dark) {
mDark = dark;
mKeyguardStatusView.setDark(dark);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index e86fd48..d342635 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -221,8 +221,11 @@
public void setTouchDisabled(boolean disabled) {
mTouchDisabled = disabled;
- if (mTouchDisabled && mTracking) {
- onTrackingStopped(true /* expanded */);
+ if (mTouchDisabled) {
+ cancelHeightAnimator();
+ if (mTracking) {
+ onTrackingStopped(true /* expanded */);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 5690495..41fb5f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -579,6 +579,7 @@
.setComponent(aiaComponent)
.setAction(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
+ .addCategory("unique:" + System.currentTimeMillis())
.putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName)
.putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode)
.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent);
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 c1859fe..b33d509 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -172,6 +172,7 @@
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.RowInflaterTask;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
@@ -1588,12 +1589,12 @@
private void abortExistingInflation(String key) {
if (mPendingNotifications.containsKey(key)) {
Entry entry = mPendingNotifications.get(key);
- entry.abortInflation();
+ entry.abortTask();
mPendingNotifications.remove(key);
}
Entry addedEntry = mNotificationData.get(key);
if (addedEntry != null) {
- addedEntry.abortInflation();
+ addedEntry.abortTask();
}
}
@@ -1610,7 +1611,7 @@
}
@Override
- public void handleInflationException(StatusBarNotification notification, InflationException e) {
+ public void handleInflationException(StatusBarNotification notification, Exception e) {
handleNotificationError(notification, e.getMessage());
}
@@ -2489,29 +2490,28 @@
}
StringBuilder flagdbg = new StringBuilder();
- flagdbg.append("disable: < ");
- flagdbg.append(((state1 & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
- flagdbg.append(((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
- flagdbg.append(((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
- flagdbg.append(((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
- flagdbg.append(((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
- flagdbg.append(((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
- flagdbg.append(((state1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
- flagdbg.append(((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
- flagdbg.append(((state1 & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
- flagdbg.append(((diff1 & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
- flagdbg.append(((state1 & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
- flagdbg.append(((diff1 & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
- flagdbg.append(((state1 & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
- flagdbg.append(((diff1 & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
- flagdbg.append(((state1 & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
- flagdbg.append(((diff1 & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
- flagdbg.append(((state1 & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search");
- flagdbg.append(((diff1 & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " ");
- flagdbg.append(((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) ? "QUICK_SETTINGS"
- : "quick_settings");
- flagdbg.append(((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) ? "* " : " ");
- flagdbg.append(">");
+ flagdbg.append("disable<");
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_EXPAND)) ? 'E' : 'e');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_EXPAND)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? 'I' : 'i');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? 'A' : 'a');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? 'S' : 's');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_BACK)) ? 'B' : 'b');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_BACK)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_HOME)) ? 'H' : 'h');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_HOME)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_RECENT)) ? 'R' : 'r');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_RECENT)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_CLOCK)) ? 'C' : 'c');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_CLOCK)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH)) ? 'S' : 's');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SEARCH)) ? '!' : ' ');
+ flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? 'Q' : 'q');
+ flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? '!' : ' ');
+ flagdbg.append('>');
Log.d(TAG, flagdbg.toString());
if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
@@ -2996,8 +2996,9 @@
mStatusBarWindowManager.setPanelVisible(false);
mStatusBarWindowManager.setForceStatusBarVisible(false);
- // Close any "App info" popups that might have snuck on-screen
- dismissPopups();
+ // Close any guts that might be visible
+ closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, true /* removeControls */,
+ -1 /* x */, -1 /* y */, true /* resetMenu */);
runPostCollapseRunnables();
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
@@ -4056,13 +4057,6 @@
setBarState(StatusBarState.KEYGUARD);
}
updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
- if (!mDeviceInteractive) {
-
- // If the screen is off already, we need to disable touch events because these might
- // collapse the panel after we expanded it, and thus we would end up with a blank
- // Keyguard.
- mNotificationPanel.setTouchDisabled(true);
- }
if (mState == StatusBarState.KEYGUARD) {
instantExpandNotificationsPanel();
} else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
@@ -4617,6 +4611,7 @@
public void onDragDownReset() {
mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
mStackScroller.resetScrollPosition();
+ mStackScroller.resetCheckSnoozeLeavebehind();
}
@Override
@@ -4627,6 +4622,7 @@
@Override
public void onTouchSlopExceeded() {
mStackScroller.removeLongPressCallback();
+ mStackScroller.checkSnoozeLeavebehind();
}
@Override
@@ -4861,6 +4857,12 @@
mStackScroller.setAnimationsEnabled(false);
mVisualStabilityManager.setScreenOn(false);
updateVisibleToUser();
+
+ // We need to disable touch events because these might
+ // collapse the panel after we expanded it, and thus we would end up with a blank
+ // Keyguard.
+ mNotificationPanel.setTouchDisabled(true);
+ mStatusBarWindow.cancelCurrentTouch();
if (mLaunchCameraOnFinishedGoingToSleep) {
mLaunchCameraOnFinishedGoingToSleep = false;
@@ -5800,8 +5802,10 @@
if (!g.willBeRemoved() && !row.isRemoved()) {
mStackScroller.onHeightChanged(row, !isPanelFullyCollapsed() /* needsAnimation */);
}
- mNotificationGutsExposed = null;
- mGutsMenuItem = null;
+ if (mNotificationGutsExposed == g) {
+ mNotificationGutsExposed = null;
+ mGutsMenuItem = null;
+ }
});
View gutsView = item.getGutsView();
@@ -5897,7 +5901,8 @@
final int centerY = done.getHeight() / 2;
final int x = doneLocation[0] - rowLocation[0] + centerX;
final int y = doneLocation[1] - rowLocation[1] + centerY;
- dismissPopups(x, y);
+ closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
+ true /* removeControls */, x, y, true /* resetMenu */);
}
protected SwipeHelper.LongPressListener getNotificationLongClicker() {
@@ -5917,6 +5922,12 @@
if (row.isDark()) {
return false;
}
+ if (row.areGutsExposed()) {
+ closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */,
+ true /* resetMenu */);
+ return false;
+ }
bindGuts(row, item);
NotificationGuts guts = row.getGuts();
@@ -5926,12 +5937,6 @@
return false;
}
- // Already showing?
- if (guts.getVisibility() == View.VISIBLE) {
- dismissPopups(x, y);
- return false;
- }
-
mMetricsLogger.action(MetricsEvent.ACTION_NOTE_CONTROLS);
// ensure that it's laid but not visible until actually laid out
@@ -5945,8 +5950,9 @@
+ "window");
return;
}
- dismissPopups(-1 /* x */, -1 /* y */, false /* resetMenu */,
- false /* animate */);
+ closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
guts.setVisibility(View.VISIBLE);
final double horz = Math.max(guts.getWidth() - x, x);
final double vert = Math.max(guts.getHeight() - y, y);
@@ -5986,20 +5992,23 @@
return mNotificationGutsExposed;
}
- public void dismissPopups() {
- dismissPopups(-1 /* x */, -1 /* y */, true /* resetMenu */, false /* animate */);
- }
-
- private void dismissPopups(int x, int y) {
- dismissPopups(x, y, true /* resetMenu */, false /* animate */);
- }
-
- public void dismissPopups(int x, int y, boolean resetMenu, boolean animate) {
+ /**
+ * Closes guts or notification menus that might be visible and saves any changes.
+ *
+ * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
+ * @param force true if guts should be closed regardless of state (used for snooze only).
+ * @param removeControls true if controls (e.g. info) should be closed.
+ * @param x if closed based on touch location, this is the x touch location.
+ * @param y if closed based on touch location, this is the y touch location.
+ * @param resetMenu if any notification menus that might be revealed should be closed.
+ */
+ public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
+ int x, int y, boolean resetMenu) {
if (mNotificationGutsExposed != null) {
- mNotificationGutsExposed.closeControls(x, y, true /* save */);
+ mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
}
if (resetMenu) {
- mStackScroller.resetExposedMenuView(animate, true /* force */);
+ mStackScroller.resetExposedMenuView(false /* animate */, true /* force */);
}
}
@@ -6173,50 +6182,57 @@
entry.notification.getUser().getIdentifier());
final StatusBarNotification sbn = entry.notification;
- ExpandableNotificationRow row;
if (entry.row != null) {
- row = entry.row;
entry.reset();
+ updateNotification(entry, pmUser, sbn, entry.row);
} else {
- // create the row view
- LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
- parent, false);
- row.setExpansionLogger(this, entry.notification.getKey());
- row.setGroupManager(mGroupManager);
- row.setHeadsUpManager(mHeadsUpManager);
- row.setRemoteInputController(mRemoteInputController);
- row.setOnExpandClickListener(this);
- row.setRemoteViewClickHandler(mOnClickHandler);
- row.setInflationCallback(this);
-
- // Get the app name.
- // Note that Notification.Builder#bindHeaderAppName has similar logic
- // but since this field is used in the guts, it must be accurate.
- // Therefore we will only show the application label, or, failing that, the
- // package name. No substitutions.
- final String pkg = sbn.getPackageName();
- String appname = pkg;
- try {
- final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- if (info != null) {
- appname = String.valueOf(pmUser.getApplicationLabel(info));
- }
- } catch (NameNotFoundException e) {
- // Do nothing
- }
- row.setAppName(appname);
- row.setOnDismissRunnable(() ->
- performRemoveNotification(row.getStatusBarNotification()));
- row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- if (ENABLE_REMOTE_INPUT) {
- row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
- }
+ new RowInflaterTask().inflate(mContext, parent, entry,
+ row -> {
+ bindRow(entry, pmUser, sbn, row);
+ updateNotification(entry, pmUser, sbn, row);
+ });
}
+ }
+
+ private void bindRow(Entry entry, PackageManager pmUser,
+ StatusBarNotification sbn, ExpandableNotificationRow row) {
+ row.setExpansionLogger(this, entry.notification.getKey());
+ row.setGroupManager(mGroupManager);
+ row.setHeadsUpManager(mHeadsUpManager);
+ row.setRemoteInputController(mRemoteInputController);
+ row.setOnExpandClickListener(this);
+ row.setRemoteViewClickHandler(mOnClickHandler);
+ row.setInflationCallback(this);
+
+ // Get the app name.
+ // Note that Notification.Builder#bindHeaderAppName has similar logic
+ // but since this field is used in the guts, it must be accurate.
+ // Therefore we will only show the application label, or, failing that, the
+ // package name. No substitutions.
+ final String pkg = sbn.getPackageName();
+ String appname = pkg;
+ try {
+ final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS);
+ if (info != null) {
+ appname = String.valueOf(pmUser.getApplicationLabel(info));
+ }
+ } catch (NameNotFoundException e) {
+ // Do nothing
+ }
+ row.setAppName(appname);
+ row.setOnDismissRunnable(() ->
+ performRemoveNotification(row.getStatusBarNotification()));
+ row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ if (ENABLE_REMOTE_INPUT) {
+ row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
+ }
+ }
+
+ private void updateNotification(Entry entry, PackageManager pmUser,
+ StatusBarNotification sbn, ExpandableNotificationRow row) {
row.setNeedsRedaction(needsRedaction(entry));
boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
row.setIsLowPriority(isLowPriority);
@@ -6521,7 +6537,8 @@
if (mVisible != visible) {
mVisible = visible;
if (!visible) {
- dismissPopups();
+ closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
}
}
updateVisibleToUser();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 1a09d75..26e007c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -37,6 +37,7 @@
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.ActionMode;
+import android.view.InputDevice;
import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -84,6 +85,8 @@
private ActionMode mFloatingActionMode;
private FloatingToolbar mFloatingToolbar;
private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
+ private boolean mTouchCancelled;
+ private boolean mTouchActive;
public StatusBarWindowView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -239,10 +242,20 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getActionMasked() == MotionEvent.ACTION_DOWN
- && mNotificationPanel.isFullyCollapsed()) {
+ boolean isDown = ev.getActionMasked() == MotionEvent.ACTION_DOWN;
+ if (isDown && mNotificationPanel.isFullyCollapsed()) {
mNotificationPanel.startExpandLatencyTracking();
}
+ if (isDown) {
+ mTouchActive = true;
+ mTouchCancelled = false;
+ } else if (ev.getActionMasked() == MotionEvent.ACTION_UP
+ || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+ mTouchActive = false;
+ }
+ if (mTouchCancelled) {
+ return false;
+ }
mFalsingManager.onTouchEvent(ev, getWidth(), getHeight());
if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) {
// Disallow new pointers while the brightness mirror is visible. This is so that you
@@ -252,7 +265,7 @@
return false;
}
}
- if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ if (isDown) {
mStackScrollLayout.closeControlsIfOutsideTouch(ev);
}
if (mService.isDozing()) {
@@ -349,6 +362,18 @@
}
}
+ public void cancelCurrentTouch() {
+ if (mTouchActive) {
+ final long now = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(now, now,
+ MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ dispatchTouchEvent(event);
+ event.recycle();
+ mTouchCancelled = true;
+ }
+ }
+
public class LayoutParams extends FrameLayout.LayoutParams {
public boolean ignoreRightInset;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
index a2d1baf..c726189 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
@@ -47,6 +47,11 @@
public void onPhoneStateChanged(int phoneState) {
update();
}
+
+ @Override
+ public void onRefreshCarrierInfo() {
+ update();
+ }
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 15fcb38..5f83e3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -75,6 +75,7 @@
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationGuts;
import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationSnooze;
import com.android.systemui.statusbar.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.FakeShadowView;
@@ -234,6 +235,7 @@
private NotificationMenuRowPlugin mCurrMenuRow;
private View mTranslatingParentView;
private View mMenuExposedView;
+ boolean mCheckForLeavebehind;
/**
* Should in this touch motion only be scrolling allowed? It's true when the scroller was
@@ -1310,6 +1312,22 @@
&& !mDisallowDismissInThisMotion) {
horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
}
+
+ // Check if we need to clear any snooze leavebehinds
+ NotificationGuts guts = mStatusBar.getExposedGuts();
+ if (guts != null && !isTouchInView(ev, guts)
+ && guts.getGutsContent() instanceof NotificationSnooze) {
+ NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
+ if ((ns.isExpanded() && isCancelOrUp)
+ || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
+ // If the leavebehind is expanded we clear it on the next up event, otherwise we
+ // clear it on the next non-horizontal swipe or expand event.
+ checkSnoozeLeavebehind();
+ }
+ }
+ if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+ mCheckForLeavebehind = true;
+ }
return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
}
@@ -1429,6 +1447,8 @@
// existing overScroll, we have to scroll the view
customOverScrollBy((int) scrollAmount, mOwnScrollY,
range, getHeight() / 2);
+ // If we're scrolling, leavebehinds should be dismissed
+ checkSnoozeLeavebehind();
}
}
break;
@@ -2458,6 +2478,18 @@
&& !mDisallowDismissInThisMotion) {
swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
}
+ // Check if we need to clear any snooze leavebehinds
+ boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
+ NotificationGuts guts = mStatusBar.getExposedGuts();
+ if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
+ && !scrollWantsIt) {
+ mCheckForLeavebehind = false;
+ mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
+ false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+ }
+ if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+ mCheckForLeavebehind = true;
+ }
return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
}
@@ -3287,13 +3319,27 @@
return Math.max(mMaxLayoutHeight - mContentHeight, 0);
}
+ public void checkSnoozeLeavebehind() {
+ if (mCheckForLeavebehind) {
+ mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
+ false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+ mCheckForLeavebehind = false;
+ }
+ }
+
+ public void resetCheckSnoozeLeavebehind() {
+ mCheckForLeavebehind = true;
+ }
+
public void onExpansionStarted() {
mIsExpansionChanging = true;
mAmbientState.setExpansionChanging(true);
+ checkSnoozeLeavebehind();
}
public void onExpansionStopped() {
mIsExpansionChanging = false;
+ resetCheckSnoozeLeavebehind();
mAmbientState.setExpansionChanging(false);
if (!mIsExpanded) {
setOwnScrollY(0);
@@ -4292,6 +4338,8 @@
// of the panel early.
handleChildDismissed(view);
}
+ mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
+ false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
handleMenuCoveredOrDismissed();
}
@@ -4374,29 +4422,19 @@
public void closeControlsIfOutsideTouch(MotionEvent ev) {
NotificationGuts guts = mStatusBar.getExposedGuts();
View view = null;
- int height = 0;
- if (guts != null) {
- // Checking guts
+ if (guts != null && !guts.getGutsContent().isLeavebehind()) {
+ // Only close visible guts if they're not a leavebehind.
view = guts;
- height = guts.getActualHeight();
} else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
&& mTranslatingParentView != null) {
// Checking menu
view = mTranslatingParentView;
- height = ((ExpandableView) mTranslatingParentView).getActualHeight();
}
- if (view != null) {
- final int rx = (int) ev.getRawX();
- final int ry = (int) ev.getRawY();
-
- view.getLocationOnScreen(mTempInt2);
- final int x = mTempInt2[0];
- final int y = mTempInt2[1];
- Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
- if (!rect.contains(rx, ry)) {
- // Touch was outside visible guts / meny notification, close what's visible
- mStatusBar.dismissPopups(-1, -1, true /* resetMenu */, true /* animate */);
- }
+ if (view != null && !isTouchInView(ev, view)) {
+ // Touch was outside visible guts / menu notification, close what's visible
+ mStatusBar.closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+ resetExposedMenuView(true /* animate */, true /* force */);
}
}
@@ -4420,6 +4458,23 @@
}
}
+ private boolean isTouchInView(MotionEvent ev, View view) {
+ if (view == null) {
+ return false;
+ }
+ final int height = (view instanceof ExpandableView)
+ ? ((ExpandableView) view).getActualHeight()
+ : view.getHeight();
+ final int rx = (int) ev.getRawX();
+ final int ry = (int) ev.getRawY();
+ view.getLocationOnScreen(mTempInt2);
+ final int x = mTempInt2[0];
+ final int y = mTempInt2[1];
+ Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
+ boolean ret = rect.contains(rx, ry);
+ return ret;
+ }
+
private void updateContinuousShadowDrawing() {
boolean continuousShadowUpdate = mAnimationRunning
|| !mAmbientState.getDraggedViews().isEmpty();
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index cd85a76..ae8afe4 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -30,6 +30,7 @@
public static String SCREENSHOTS = "SCN";
public static String GENERAL = "GEN";
public static String STORAGE = "DSK";
+ public static String TVPIP = "TPP";
@VisibleForTesting
static void createAll(Context context) {
@@ -55,6 +56,15 @@
? NotificationManager.IMPORTANCE_DEFAULT
: NotificationManager.IMPORTANCE_LOW)
));
+ if (isTv(context)) {
+ // TV specific notification channel for TV PIP controls.
+ // Importance should be {@link NotificationManager#IMPORTANCE_MAX} to have the highest
+ // priority, so it can be shown in all times.
+ nm.createNotificationChannel(new NotificationChannel(
+ TVPIP,
+ context.getString(R.string.notification_channel_tv_pip),
+ NotificationManager.IMPORTANCE_MAX));
+ }
}
@Override
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 5e8b3f9..5e71dd4 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -38,6 +38,7 @@
LOCAL_STATIC_ANDROID_LIBRARIES := \
SystemUIPluginLib \
+ android-support-v4 \
android-support-v7-recyclerview \
android-support-v7-preference \
android-support-v7-appcompat \
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 2b14b31..0531ec5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -497,7 +497,7 @@
mNotificationChannel.getImportance(), mSbn, null, null, null,
null, null);
- mNotificationInfo.handleCloseControls(true);
+ mNotificationInfo.handleCloseControls(true, false);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), anyInt(), any());
}
@@ -511,7 +511,7 @@
mNotificationChannel.getImportance(), mSbn, null, null, null,
null, null);
- mNotificationInfo.handleCloseControls(true);
+ mNotificationInfo.handleCloseControls(true, false);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), anyInt(), any());
}
@@ -571,7 +571,7 @@
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
mNotificationChannel.getImportance(), mSbn, null, null, null,
null, Collections.singleton(TEST_PACKAGE_NAME));
- mNotificationInfo.handleCloseControls(true);
+ mNotificationInfo.handleCloseControls(true, false);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), anyInt(), any());
}
@@ -586,7 +586,7 @@
Switch enabledSwitch = mNotificationInfo.findViewById(R.id.channel_enabled_switch);
enabledSwitch.setChecked(false);
- mNotificationInfo.handleCloseControls(true);
+ mNotificationInfo.handleCloseControls(true, false);
ArgumentCaptor<NotificationChannel> updated =
ArgumentCaptor.forClass(NotificationChannel.class);
@@ -606,7 +606,7 @@
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
enabledSwitch.setChecked(false);
- mNotificationInfo.handleCloseControls(false);
+ mNotificationInfo.handleCloseControls(false, false);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel));
}
@@ -623,7 +623,7 @@
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
enabledSwitch.setChecked(false);
- mNotificationInfo.handleCloseControls(true);
+ mNotificationInfo.handleCloseControls(true, false);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel));
}
@@ -641,7 +641,7 @@
Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
enabledSwitch.setChecked(false);
- mNotificationInfo.handleCloseControls(true);
+ mNotificationInfo.handleCloseControls(true, false);
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
index fbb25e5..15381b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.widget.RemoteViews;
@@ -41,7 +42,6 @@
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
-import java.util.function.Function;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -67,7 +67,7 @@
mNotificationInflater.setInflationCallback(new NotificationInflater.InflationCallback() {
@Override
public void handleInflationException(StatusBarNotification notification,
- InflationException e) {
+ Exception e) {
}
@Override
@@ -77,6 +77,7 @@
}
@Test
+ @UiThreadTest
public void testIncreasedHeadsUpBeingUsed() {
mNotificationInflater.setUsesIncreasedHeadsUpHeight(true);
Notification.Builder builder = spy(mBuilder);
@@ -85,6 +86,7 @@
}
@Test
+ @UiThreadTest
public void testIncreasedHeightBeingUsed() {
mNotificationInflater.setUsesIncreasedHeight(true);
Notification.Builder builder = spy(mBuilder);
@@ -124,10 +126,10 @@
@Test
public void testAsyncTaskRemoved() throws Exception {
- mRow.getEntry().abortInflation();
+ mRow.getEntry().abortTask();
runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
mNotificationInflater);
- Assert.assertTrue(mRow.getEntry().getRunningTasks().size() == 0);
+ Assert.assertNull(mRow.getEntry().getRunningTask() );
}
public static void runThenWaitForInflation(Runnable block,
@@ -143,7 +145,7 @@
inflater.setInflationCallback(new NotificationInflater.InflationCallback() {
@Override
public void handleInflationException(StatusBarNotification notification,
- InflationException e) {
+ Exception e) {
if (!expectingException) {
exceptionHolder.setException(e);
}
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 53b3fe9..2f6b7e6 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -212,6 +212,10 @@
// Package: com.android.systemui
NOTE_LOGOUT_USER = 1011;
+ // Notify the user that a TV PIP is running.
+ // Package: com.android.systemui
+ NOTE_TV_PIP = 1100;
+
// Communicate to the user about remote bugreports.
// Package: android
NOTE_REMOTE_BUGREPORT = 678432343;
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 05ad161..238bf0f 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -2895,6 +2895,7 @@
mAllocationArray[0] = createTyped(rs, t, usage);
if ((usage & USAGE_IO_INPUT) != 0) {
if (numAlloc > MAX_NUMBER_IO_INPUT_ALLOC) {
+ mAllocationArray[0].destroy();
throw new RSIllegalArgumentException("Exceeds the max number of Allocations allowed: " +
MAX_NUMBER_IO_INPUT_ALLOC);
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index ac81565..b2712ff 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -373,11 +373,6 @@
}
private void onPackageBroadcastReceived(Intent intent, int userId) {
- if (!mUserManager.isUserUnlockingOrUnlocked(userId) ||
- isProfileWithLockedParent(userId)) {
- return;
- }
-
final String action = intent.getAction();
boolean added = false;
boolean changed = false;
@@ -408,7 +403,11 @@
}
synchronized (mLock) {
- ensureGroupStateLoadedLocked(userId);
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId) ||
+ isProfileWithLockedParent(userId)) {
+ return;
+ }
+ ensureGroupStateLoadedLocked(userId, /* enforceUserUnlockingOrUnlocked */ false);
Bundle extras = intent.getExtras();
@@ -844,7 +843,7 @@
mSecurityPolicy.enforceCallFromPackage(callingPackage);
synchronized (mLock) {
- ensureGroupStateLoadedLocked(userId);
+ ensureGroupStateLoadedLocked(userId, /* enforceUserUnlockingOrUnlocked */ false);
// NOTE: The lookup is enforcing security across users by making
// sure the caller can only access hosts it owns.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index b536ad9..e3398c9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -118,7 +118,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
- mUi.hideAll();
+ mUi.hideAll(null);
}
}
};
@@ -213,7 +213,7 @@
if (!doit) {
return true;
}
- handleActiveAutofillServiceRemoved(getChangingUserId());
+ removeCachedServiceLocked(getChangingUserId());
}
}
}
@@ -470,9 +470,9 @@
}
@Override
- public int startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback,
- AutofillId autofillId, Rect bounds, AutofillValue value, int userId,
- boolean hasCallback, int flags, String packageName) {
+ public int startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId,
+ Rect bounds, AutofillValue value, int userId, boolean hasCallback, int flags,
+ String packageName) {
activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
@@ -489,8 +489,8 @@
synchronized (mLock) {
final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
- return service.startSessionLocked(activityToken, getCallingUid(), windowToken,
- appCallback, autofillId, bounds, value, hasCallback, flags, packageName);
+ return service.startSessionLocked(activityToken, getCallingUid(), appCallback,
+ autofillId, bounds, value, hasCallback, flags, packageName);
}
}
@@ -528,19 +528,6 @@
}
@Override
- public void setWindow(int sessionId, IBinder windowToken) throws RemoteException {
- windowToken = Preconditions.checkNotNull(windowToken, "windowToken");
-
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = mServicesCache.get(
- UserHandle.getCallingUserId());
- if (service != null) {
- service.setWindow(sessionId, getCallingUid(), windowToken);
- }
- }
- }
-
- @Override
public void updateSession(int sessionId, AutofillId id, Rect bounds,
AutofillValue value, int action, int flags, int userId) {
synchronized (mLock) {
@@ -602,6 +589,31 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ boolean showHistory = true;
+ boolean uiOnly = false;
+ if (args != null) {
+ for (String arg : args) {
+ switch(arg) {
+ case "--no-history":
+ showHistory = false;
+ break;
+ case "--ui-only":
+ uiOnly = true;
+ break;
+ case "--help":
+ pw.println("Usage: dumpsys autofill [--ui-only|--no-history]");
+ return;
+ default:
+ throw new IllegalArgumentException("Invalid dump arg: " + arg);
+ }
+ }
+ }
+
+ if (uiOnly) {
+ mUi.dump(pw);
+ return;
+ }
+
boolean oldDebug = sDebug;
try {
synchronized (mLock) {
@@ -624,8 +636,10 @@
}
mUi.dump(pw);
}
- pw.println("Requests history:");
- mRequestsHistory.reverseDump(fd, pw, args);
+ if (showHistory) {
+ pw.println("Requests history:");
+ mRequestsHistory.reverseDump(fd, pw, args);
+ }
} finally {
setDebugLocked(oldDebug);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4507eae..e315f9d 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -19,17 +19,21 @@
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.NO_SESSION;
+import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.AppGlobals;
+import android.app.IActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.graphics.Rect;
+import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -46,6 +50,7 @@
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -72,6 +77,9 @@
private static final String TAG = "AutofillManagerServiceImpl";
private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
+ /** Minimum interval to prune abandoned sessions */
+ private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;
+
static final int MSG_SERVICE_SAVE = 1;
private final int mUserId;
@@ -104,10 +112,10 @@
mHandlerCallback, true);
/**
- * Cache of pending {@link Session}s, keyed by {@code activityToken}.
+ * Cache of pending {@link Session}s, keyed by sessionId.
*
* <p>They're kept until the {@link AutofillService} finished handling a request, an error
- * occurs, or the session times out.
+ * occurs, or the session is abandoned.
*/
@GuardedBy("mLock")
private final SparseArray<Session> mSessions = new SparseArray<>();
@@ -116,6 +124,9 @@
@GuardedBy("mLock")
private FillEventHistory mEventHistory;
+ /** When was {@link PruneTask} last executed? */
+ private long mLastPrune = 0;
+
AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
int userId, AutoFillUI ui, boolean disabled) {
mContext = context;
@@ -252,7 +263,7 @@
}
}
- int startSessionLocked(@NonNull IBinder activityToken, int uid, @Nullable IBinder windowToken,
+ int startSessionLocked(@NonNull IBinder activityToken, int uid,
@NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
@NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
int flags, @NonNull String packageName) {
@@ -260,8 +271,11 @@
return 0;
}
- final Session newSession = createSessionByTokenLocked(activityToken, uid, windowToken,
- appCallbackToken, hasCallback, flags, packageName);
+ // Occasionally clean up abandoned sessions
+ pruneAbandonedSessionsLocked();
+
+ final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken,
+ hasCallback, flags, packageName);
if (newSession == null) {
return NO_SESSION;
}
@@ -277,6 +291,20 @@
return newSession.id;
}
+ /**
+ * Remove abandoned sessions if needed.
+ */
+ private void pruneAbandonedSessionsLocked() {
+ long now = System.currentTimeMillis();
+ if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) {
+ mLastPrune = now;
+
+ if (mSessions.size() > 0) {
+ (new PruneTask()).execute();
+ }
+ }
+ }
+
void finishSessionLocked(int sessionId, int uid) {
if (!isEnabled()) {
return;
@@ -331,8 +359,8 @@
}
private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid,
- @Nullable IBinder windowToken, @NonNull IBinder appCallbackToken, boolean hasCallback,
- int flags, @NonNull String packageName) {
+ @NonNull IBinder appCallbackToken, boolean hasCallback, int flags,
+ @NonNull String packageName) {
// use random ids so that one app cannot know that another app creates sessions
int sessionId;
int tries = 0;
@@ -347,7 +375,7 @@
} while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0);
final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
- sessionId, uid, activityToken, windowToken, appCallbackToken, hasCallback,
+ sessionId, uid, activityToken, appCallbackToken, hasCallback,
mInfo.getServiceInfo().getComponentName(), packageName);
mSessions.put(newSession.id, newSession);
@@ -374,24 +402,6 @@
}
}
- /**
- * Set the window the UI should get attached to
- *
- * @param sessionId The id of the session to restore
- * @param uid UID of the process that tries to restore the session
- * @param windowToken The window the activity is now in
- */
- boolean setWindow(int sessionId, int uid, @NonNull IBinder windowToken) {
- final Session session = mSessions.get(sessionId);
-
- if (session == null || uid != session.uid) {
- return false;
- } else {
- session.switchWindow(windowToken);
- return true;
- }
- }
-
void updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds,
AutofillValue value, int action, int flags) {
final Session session = mSessions.get(sessionId);
@@ -513,6 +523,7 @@
pw.print(prefix); pw.print("Default component: ");
pw.println(mContext.getString(R.string.config_defaultAutofillService));
pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+ pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
final int size = mSessions.size();
if (size == 0) {
@@ -604,4 +615,62 @@
+ ", component=" + (mInfo != null
? mInfo.getServiceInfo().getComponentName() : null) + "]";
}
+
+ /** Task used to prune abandoned session */
+ private class PruneTask extends AsyncTask<Void, Void, Void> {
+ @Override
+ protected Void doInBackground(Void... ignored) {
+ int numSessionsToRemove;
+ ArrayMap<IBinder, Integer> sessionsToRemove;
+
+ synchronized (mLock) {
+ numSessionsToRemove = mSessions.size();
+ sessionsToRemove = new ArrayMap<>(numSessionsToRemove);
+
+ for (int i = 0; i < numSessionsToRemove; i++) {
+ Session session = mSessions.valueAt(i);
+
+ sessionsToRemove.put(session.getActivityTokenLocked(), session.id);
+ }
+ }
+
+ IActivityManager am = ActivityManager.getService();
+
+ // Only remove sessions which's activities are not known to the activity manager anymore
+ for (int i = 0; i < numSessionsToRemove; i++) {
+ try {
+ // The activity manager cannot resolve activities that have been removed
+ if (am.getActivityClassForToken(sessionsToRemove.keyAt(i)) != null) {
+ sessionsToRemove.removeAt(i);
+ i--;
+ numSessionsToRemove--;
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Cannot figure out if activity is finished", e);
+ }
+ }
+
+ synchronized (mLock) {
+ for (int i = 0; i < numSessionsToRemove; i++) {
+ Session sessionToRemove = mSessions.get(sessionsToRemove.valueAt(i));
+
+ if (sessionToRemove != null) {
+ if (sessionToRemove.isSavingLocked()) {
+ if (sVerbose) {
+ Slog.v(TAG, "Session " + sessionToRemove.id + " is saving");
+ }
+ } else {
+ if (sDebug) {
+ Slog.i(TAG, "Prune session " + sessionToRemove.id + " ("
+ + sessionToRemove.getActivityTokenLocked() + ")");
+ }
+ sessionToRemove.removeSelfLocked();
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index cbf97dd..8d947b9 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -20,9 +20,7 @@
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.os.Bundle;
-import android.util.DebugUtils;
import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillManager;
import java.util.Arrays;
import java.util.Objects;
@@ -68,10 +66,6 @@
return builder.toString();
}
- static String getUpdateActionAsString(int action) {
- return DebugUtils.flagsToString(AutofillManager.class, "ACTION_", action);
- }
-
static ViewNode findViewNodeById(@NonNull AssistStructure structure, @NonNull AutofillId id) {
final int size = structure.getWindowNodeCount();
for (int i = 0; i < size; i++) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 23b734a..3f78fb8 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 com.android.server.autofill.Helper.findViewNodeById;
-import static com.android.server.autofill.Helper.getUpdateActionAsString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.ViewState.STATE_AUTOFILLED;
@@ -120,9 +119,6 @@
@GuardedBy("mLock")
@NonNull private IBinder mActivityToken;
- @GuardedBy("mLock")
- @NonNull private IBinder mWindowToken;
-
/** Package name of the app that is auto-filled */
@NonNull private final String mPackageName;
@@ -177,6 +173,10 @@
@GuardedBy("mLock")
private boolean mDestroyed;
+ /** Whether the session is currently saving */
+ @GuardedBy("mLock")
+ private boolean mIsSaving;
+
/**
* Receiver of assist data from the app's {@link Activity}.
*/
@@ -338,7 +338,7 @@
Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui,
@NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId,
@NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken,
- @Nullable IBinder windowToken, @NonNull IBinder client, boolean hasCallback,
+ @NonNull IBinder client, boolean hasCallback,
@NonNull ComponentName componentName, @NonNull String packageName) {
id = sessionId;
this.uid = uid;
@@ -348,7 +348,6 @@
mHandlerCaller = handlerCaller;
mRemoteFillService = new RemoteFillService(context, componentName, userId, this);
mActivityToken = activityToken;
- mWindowToken = windowToken;
mHasCallback = hasCallback;
mPackageName = packageName;
mClient = IAutoFillManagerClient.Stub.asInterface(client);
@@ -361,28 +360,11 @@
*
* @return The activity token
*/
- IBinder getActivityTokenLocked() {
+ @NonNull IBinder getActivityTokenLocked() {
return mActivityToken;
}
/**
- * Sets new window for this session.
- *
- * @param newWindow The window the Ui should be attached to. Can be {@code null} if no
- * further UI is needed.
- */
- void switchWindow(@NonNull IBinder newWindow) {
- synchronized (mLock) {
- if (mDestroyed) {
- Slog.w(TAG, "Call to Session#switchWindow() rejected - session: "
- + id + " destroyed");
- return;
- }
- mWindowToken = newWindow;
- }
- }
-
- /**
* Sets new activity and client for this session.
*
* @param newActivity The token of the new activity
@@ -416,7 +398,7 @@
}
if (response == null) {
if ((requestFlags & FLAG_MANUAL_REQUEST) != 0) {
- getUiForShowing().showError(R.string.autofill_error_cannot_autofill);
+ getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this);
}
// Nothing to be done, but need to notify client.
notifyUnavailableToClient();
@@ -466,7 +448,7 @@
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
mMetricsLogger.write(log);
- getUiForShowing().showError(message);
+ getUiForShowing().showError(message, this);
removeSelf();
}
@@ -474,6 +456,8 @@
@Override
public void onSaveRequestSuccess(@NonNull String servicePackageName) {
synchronized (mLock) {
+ mIsSaving = false;
+
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onSaveRequestSuccess() rejected - session: "
+ id + " destroyed");
@@ -496,6 +480,8 @@
public void onSaveRequestFailure(@Nullable CharSequence message,
@NonNull String servicePackageName) {
synchronized (mLock) {
+ mIsSaving = false;
+
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onSaveRequestFailure() rejected - session: "
+ id + " destroyed");
@@ -509,7 +495,7 @@
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
mMetricsLogger.write(log);
- getUiForShowing().showError(message);
+ getUiForShowing().showError(message, this);
removeSelf();
}
@@ -596,6 +582,8 @@
@Override
public void cancelSave() {
synchronized (mLock) {
+ mIsSaving = false;
+
if (mDestroyed) {
Slog.w(TAG, "Call to Session#cancelSave() rejected - session: "
+ id + " destroyed");
@@ -618,8 +606,8 @@
if (id.equals(mCurrentViewId)) {
try {
final ViewState view = mViewStates.get(id);
- mClient.requestShowFillUi(this.id, mWindowToken, id, width, height,
- view.getVirtualBounds(), presenter);
+ mClient.requestShowFillUi(this.id, id, width, height, view.getVirtualBounds(),
+ presenter);
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting to show fill UI", e);
}
@@ -639,7 +627,7 @@
// NOTE: We allow this call in a destroyed state as the UI is
// asked to go away after we get destroyed, so let it do that.
try {
- mClient.requestHideFillUi(this.id, mWindowToken, id);
+ mClient.requestHideFillUi(this.id, id);
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting to hide fill UI", e);
}
@@ -679,6 +667,8 @@
} else {
final Parcelable result = data.getParcelable(
AutofillManager.EXTRA_AUTHENTICATION_RESULT);
+ if (sVerbose) Slog.d(TAG, "setAuthenticationResultLocked() for " + result);
+
if (result instanceof FillResponse) {
FillResponse response = (FillResponse) result;
@@ -687,6 +677,16 @@
mResponseWaitingAuth = null;
if (requestIndex >= 0) {
response.setRequestId(mResponses.keyAt(requestIndex));
+ if (response.getDatasets() == null || response.getDatasets().isEmpty()) {
+ // TODO(b/37424539): there is a race condition that causes the authentication
+ // dialog to be shown again after the service authreplied with a no-datasets
+ // response. We're fixing it by hiding the UI when that happens, but that
+ // sounds like a hack - hopefully the real problem will go away when we
+ // refactor auth to support partitions; if it doesn't, we need to
+ // investigate it further (it can be reproduced by running
+ // LoginActivityTest.testFillResponseAuthServiceHasNoData())
+ mUi.hideAll(this);
+ }
processResponseLocked(response);
} else {
Slog.e(TAG, "Error cannot find id for auth response");
@@ -830,7 +830,10 @@
}
if (atLeastOneChanged) {
mService.setSaveShown();
- getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName);
+ getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName,
+ this);
+
+ mIsSaving = true;
return false;
}
}
@@ -844,6 +847,13 @@
}
/**
+ * Returns whether the session is currently showing the save UI
+ */
+ boolean isSavingLocked() {
+ return mIsSaving;
+ }
+
+ /**
* Calls service when user requested save.
*/
void callSaveLocked() {
@@ -986,18 +996,14 @@
return;
}
if (sVerbose) {
- Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + getUpdateActionAsString(action)
- + ", flags=" + flags);
+ Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + action + ", flags=" + flags);
}
ViewState viewState = mViewStates.get(id);
if (viewState == null) {
if (action == ACTION_START_SESSION || action == ACTION_VALUE_CHANGED
|| action == ACTION_VIEW_ENTERED) {
- if (sVerbose) {
- Slog.v(TAG,
- "Creating viewState for " + id + " on " + getActionAsString(action));
- }
+ if (sVerbose) Slog.v(TAG, "Creating viewState for " + id + " on " + action);
boolean isIgnored = isIgnoredLocked(id);
viewState = new ViewState(this, id, value, this,
isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
@@ -1007,7 +1013,7 @@
return;
}
} else {
- if (sVerbose) Slog.v(TAG, "Ignored " + getActionAsString(action) + " for " + id);
+ if (sVerbose) Slog.v(TAG, "Ignored action " + action + " for " + id);
return;
}
}
@@ -1036,18 +1042,21 @@
//..and the UI
if (value.isText()) {
- getUiForShowing().filterFillUi(value.getTextValue().toString());
+ getUiForShowing().filterFillUi(value.getTextValue().toString(), this);
} else {
- getUiForShowing().filterFillUi(null);
+ getUiForShowing().filterFillUi(null, this);
}
}
break;
case ACTION_VIEW_ENTERED:
+ if (sVerbose && virtualBounds != null) {
+ Slog.w(TAG, "entered on virtual child " + id + ": " + virtualBounds);
+ }
requestNewFillResponseIfNecessaryLocked(id, viewState, flags);
// Remove the UI if the ViewState has changed.
if (mCurrentViewId != viewState.id) {
- mUi.hideFillUi(mCurrentViewId != null ? mCurrentViewId : null);
+ mUi.hideFillUi(this);
mCurrentViewId = viewState.id;
}
@@ -1056,7 +1065,8 @@
break;
case ACTION_VIEW_EXITED:
if (mCurrentViewId == viewState.id) {
- mUi.hideFillUi(viewState.id);
+ if (sVerbose) Slog.d(TAG, "Exiting view " + id);
+ mUi.hideFillUi(this);
mCurrentViewId = null;
}
break;
@@ -1093,11 +1103,7 @@
filterText = value.getTextValue().toString();
}
- getUiForShowing().showFillUi(filledId, response, filterText, mPackageName);
- }
-
- String getActionAsString(int flag) {
- return DebugUtils.flagsToString(AutofillManager.class, "ACTION_", flag);
+ getUiForShowing().showFillUi(filledId, response, filterText, mPackageName, this);
}
boolean isDestroyed() {
@@ -1116,10 +1122,9 @@
synchronized (mLock) {
if (!mHasCallback) return;
try {
- mClient.notifyNoFillUi(id, mWindowToken, mCurrentViewId);
+ mClient.notifyNoFillUi(id, mCurrentViewId);
} catch (RemoteException e) {
- Slog.e(TAG, "Error notifying client no fill UI: windowToken=" + mWindowToken
- + " id=" + mCurrentViewId, e);
+ Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e);
}
}
}
@@ -1158,14 +1163,15 @@
}
private void processResponseLocked(@NonNull FillResponse response) {
+ final int requestId = response.getRequestId();
if (sVerbose) {
- Slog.v(TAG, "processResponseLocked(mCurrentViewId=" + mCurrentViewId + "):" + response);
+ Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId
+ + ", reqId=" + requestId + ", resp=" + response);
}
if (mResponses == null) {
mResponses = new SparseArray<>(4);
}
- final int requestId = response.getRequestId();
mResponses.put(requestId, response);
mClientState = response.getClientState();
@@ -1346,6 +1352,7 @@
pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId);
pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size());
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
+ pw.print(prefix); pw.print("mIsSaving: "); pw.println(mIsSaving);
final String prefix2 = prefix + " ";
for (Map.Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) {
pw.print(prefix); pw.print("State for id "); pw.println(entry.getKey());
@@ -1385,8 +1392,7 @@
}
try {
if (sDebug) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
- mClient.autofill(id, mWindowToken, dataset.getFieldIds(),
- dataset.getFieldValues());
+ mClient.autofill(id, dataset.getFieldIds(), dataset.getFieldValues());
setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED);
} catch (RemoteException e) {
Slog.w(TAG, "Error autofilling activity: " + e);
@@ -1406,8 +1412,8 @@
return;
}
mRemoteFillService.destroy();
- mUi.hideAll();
- mUi.setCallback(null);
+ mUi.hideAll(this);
+ mUi.clearCallback(this);
mDestroyed = true;
mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_FINISHED, mPackageName);
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 086742e..9eaabfe2 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -74,31 +74,43 @@
mContext = context;
}
- public void setCallback(@Nullable AutoFillUiCallback callback) {
+ public void setCallback(@NonNull AutoFillUiCallback callback) {
mHandler.post(() -> {
if (mCallback != callback) {
- hideAllUiThread();
+ if (mCallback != null) {
+ hideAllUiThread(mCallback);
+ }
+
mCallback = callback;
}
});
}
+ public void clearCallback(@NonNull AutoFillUiCallback callback) {
+ mHandler.post(() -> {
+ if (mCallback == callback) {
+ hideAllUiThread(callback);
+ mCallback = null;
+ }
+ });
+ }
+
/**
* Displays an error message to the user.
*/
- public void showError(int resId) {
- showError(mContext.getString(resId));
+ public void showError(int resId, @NonNull AutoFillUiCallback callback) {
+ showError(mContext.getString(resId), callback);
}
/**
* Displays an error message to the user.
*/
- public void showError(@Nullable CharSequence message) {
+ public void showError(@Nullable CharSequence message, @NonNull AutoFillUiCallback callback) {
mHandler.post(() -> {
- if (!hasCallback()) {
+ if (mCallback != callback) {
return;
}
- hideAllUiThread();
+ hideAllUiThread(callback);
if (!TextUtils.isEmpty(message)) {
Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
}
@@ -108,8 +120,8 @@
/**
* Hides the fill UI.
*/
- public void hideFillUi(AutofillId id) {
- mHandler.post(this::hideFillUiUiThread);
+ public void hideFillUi(@NonNull AutoFillUiCallback callback) {
+ mHandler.post(() -> hideFillUiUiThread(callback));
}
/**
@@ -117,12 +129,12 @@
*
* @param filterText The filter prefix.
*/
- public void filterFillUi(@Nullable String filterText) {
+ public void filterFillUi(@Nullable String filterText, @NonNull AutoFillUiCallback callback) {
mHandler.post(() -> {
- if (!hasCallback()) {
+ if (callback != mCallback) {
return;
}
- hideSaveUiUiThread();
+ hideSaveUiUiThread(callback);
if (mFillUi != null) {
mFillUi.setFilterText(filterText);
}
@@ -136,9 +148,11 @@
* @param response the current fill response
* @param filterText text of the view to be filled
* @param packageName package name of the activity that is filled
+ * @param callback Identifier for the caller
*/
public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
- @Nullable String filterText, @NonNull String packageName) {
+ @Nullable String filterText, @NonNull String packageName,
+ @NonNull AutoFillUiCallback callback) {
if (sDebug) {
Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + filterText);
}
@@ -150,16 +164,16 @@
response.getDatasets() == null ? 0 : response.getDatasets().size());
mHandler.post(() -> {
- if (!hasCallback()) {
+ if (callback != mCallback) {
return;
}
- hideAllUiThread();
+ hideAllUiThread(callback);
mFillUi = new FillUi(mContext, response, focusedId,
filterText, new FillUi.Callback() {
@Override
public void onResponsePicked(FillResponse response) {
log.setType(MetricsProto.MetricsEvent.TYPE_DETAIL);
- hideFillUiUiThread();
+ hideFillUiUiThread(callback);
if (mCallback != null) {
mCallback.authenticate(response.getRequestId(),
response.getAuthentication(), response.getClientState());
@@ -169,7 +183,7 @@
@Override
public void onDatasetPicked(Dataset dataset) {
log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
- hideFillUiUiThread();
+ hideFillUiUiThread(callback);
if (mCallback != null) {
mCallback.fill(response.getRequestId(), dataset);
}
@@ -178,7 +192,7 @@
@Override
public void onCanceled() {
log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS);
- hideFillUiUiThread();
+ hideFillUiUiThread(callback);
}
@Override
@@ -218,7 +232,7 @@
* Shows the UI asking the user to save for autofill.
*/
public void showSaveUi(@NonNull CharSequence providerLabel, @NonNull SaveInfo info,
- @NonNull String packageName) {
+ @NonNull String packageName, @NonNull AutoFillUiCallback callback) {
int numIds = 0;
numIds += info.getRequiredIds() == null ? 0 : info.getRequiredIds().length;
numIds += info.getOptionalIds() == null ? 0 : info.getOptionalIds().length;
@@ -228,16 +242,16 @@
MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_IDS, numIds);
mHandler.post(() -> {
- if (!hasCallback()) {
+ if (callback != mCallback) {
return;
}
- hideAllUiThread();
+ hideAllUiThread(callback);
mSaveUi = new SaveUi(mContext, providerLabel, info,
new SaveUi.OnSaveListener() {
@Override
public void onSave() {
log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
- hideSaveUiUiThread();
+ hideSaveUiUiThread(callback);
if (mCallback != null) {
mCallback.save();
}
@@ -246,7 +260,7 @@
@Override
public void onCancel(IntentSender listener) {
log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS);
- hideSaveUiUiThread();
+ hideSaveUiUiThread(callback);
if (listener != null) {
try {
listener.sendIntent(mContext, 0, null, null, null);
@@ -264,6 +278,10 @@
public void onDestroy() {
if (log.getType() == MetricsProto.MetricsEvent.TYPE_UNKNOWN) {
log.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
+
+ if (mCallback != null) {
+ mCallback.cancelSave();
+ }
}
mMetricsLogger.write(log);
}
@@ -274,46 +292,47 @@
/**
* Hides all UI affordances.
*/
- public void hideAll() {
- mHandler.post(this::hideAllUiThread);
+ public void hideAll(@Nullable AutoFillUiCallback callback) {
+ mHandler.post(() -> hideAllUiThread(callback));
}
public void dump(PrintWriter pw) {
pw.println("Autofill UI");
final String prefix = " ";
final String prefix2 = " ";
- pw.print(prefix); pw.print("showsSaveUi: "); pw.println(mSaveUi != null);
if (mFillUi != null) {
pw.print(prefix); pw.println("showsFillUi: true");
mFillUi.dump(pw, prefix2);
} else {
pw.print(prefix); pw.println("showsFillUi: false");
}
+ if (mSaveUi != null) {
+ pw.print(prefix); pw.println("showsSaveUi: true");
+ mSaveUi.dump(pw, prefix2);
+ } else {
+ pw.print(prefix); pw.println("showsSaveUi: false");
+ }
}
@android.annotation.UiThread
- private void hideFillUiUiThread() {
- if (mFillUi != null) {
+ private void hideFillUiUiThread(@Nullable AutoFillUiCallback callback) {
+ if (mFillUi != null && (callback == null || callback == mCallback)) {
mFillUi.destroy();
mFillUi = null;
}
}
@android.annotation.UiThread
- private void hideSaveUiUiThread() {
- if (mSaveUi != null) {
+ private void hideSaveUiUiThread(@Nullable AutoFillUiCallback callback) {
+ if (mSaveUi != null && (callback == null || callback == mCallback)) {
mSaveUi.destroy();
mSaveUi = null;
}
}
@android.annotation.UiThread
- private void hideAllUiThread() {
- hideFillUiUiThread();
- hideSaveUiUiThread();
- }
-
- private boolean hasCallback() {
- return mCallback != null;
+ private void hideAllUiThread(@Nullable AutoFillUiCallback callback) {
+ hideFillUiUiThread(callback);
+ hideSaveUiUiThread(callback);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index dd297a6..31b4b55 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -16,6 +16,7 @@
package com.android.server.autofill.ui;
import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -265,8 +266,11 @@
mContentWidth = 0;
mContentHeight = 0;
- final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
- final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x,
+ MeasureSpec.AT_MOST);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
+ MeasureSpec.AT_MOST);
+
final int itemCount = Math.min(mAdapter.getCount(), VISIBLE_OPTIONS_MAX_COUNT);
for (int i = 0; i < itemCount; i++) {
View view = mAdapter.getItem(i).getView();
@@ -334,6 +338,11 @@
@Override
public void show(WindowManager.LayoutParams p, Rect transitionEpicenter,
boolean fitsSystemWindows, int layoutDirection) {
+ if (sVerbose) {
+ Slog.v(TAG, "AutofillWindowPresenter.show(): fit=" + fitsSystemWindows
+ + ", epicenter="+ transitionEpicenter + ", dir=" + layoutDirection
+ + ", params=" + p);
+ }
UiThread.getHandler().post(() -> mWindow.show(p));
}
@@ -398,6 +407,7 @@
}
return false;
}
+
}
public void dump(PrintWriter pw, String prefix) {
@@ -408,5 +418,21 @@
pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth);
pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight);
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
+ pw.print(prefix); pw.print("mWindow: ");
+ if (mWindow == null) {
+ pw.println("N/A");
+ } else {
+ final String prefix2 = prefix + " ";
+ pw.println();
+ pw.print(prefix2); pw.print("showing: "); pw.println(mWindow.mShowing);
+ pw.print(prefix2); pw.print("view: "); pw.println(mWindow.mContentView);
+ pw.print(prefix2); pw.print("screen coordinates: ");
+ if (mWindow.mContentView == null) {
+ pw.println("N/A");
+ } else {
+ final int[] coordinates = mWindow.mContentView.getLocationOnScreen();
+ pw.print(coordinates[0]); pw.print("x"); pw.println(coordinates[1]);
+ }
+ }
}
}
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 bcdb118..d25ffce 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -37,6 +37,8 @@
import com.android.internal.R;
import com.android.server.UiThread;
+import java.io.PrintWriter;
+
/**
* Autofill Save Prompt
*/
@@ -96,6 +98,9 @@
private final @NonNull OneTimeListener mListener;
+ private final CharSequence mTitle;
+ private final CharSequence mSubTitle;
+
private boolean mDestroyed;
SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel, @NonNull SaveInfo info,
@@ -126,37 +131,36 @@
types.add(context.getString(R.string.autofill_save_type_email_address));
}
- final CharSequence title;
switch (types.size()) {
case 1:
- title = Html.fromHtml(context.getString(R.string.autofill_save_title_with_type,
+ mTitle = Html.fromHtml(context.getString(R.string.autofill_save_title_with_type,
types.valueAt(0), providerLabel), 0);
break;
case 2:
- title = Html.fromHtml(context.getString(R.string.autofill_save_title_with_2types,
+ mTitle = Html.fromHtml(context.getString(R.string.autofill_save_title_with_2types,
types.valueAt(0), types.valueAt(1), providerLabel), 0);
break;
case 3:
- title = Html.fromHtml(context.getString(R.string.autofill_save_title_with_3types,
+ mTitle = Html.fromHtml(context.getString(R.string.autofill_save_title_with_3types,
types.valueAt(0), types.valueAt(1), types.valueAt(2), providerLabel), 0);
break;
default:
// Use generic if more than 3 or invalid type (size 0).
- title = Html.fromHtml(
+ mTitle = Html.fromHtml(
context.getString(R.string.autofill_save_title, providerLabel), 0);
}
- titleView.setText(title);
- final CharSequence subTitle = info.getDescription();
- if (subTitle != null) {
+ titleView.setText(mTitle);
+ mSubTitle = info.getDescription();
+ if (mSubTitle != null) {
final TextView subTitleView = (TextView) view.findViewById(R.id.autofill_save_subtitle);
- subTitleView.setText(subTitle);
+ subTitleView.setText(mSubTitle);
subTitleView.setVisibility(View.VISIBLE);
}
- Slog.i(TAG, "Showing save dialog: " + title);
+ Slog.i(TAG, "Showing save dialog: " + mTitle);
if (sDebug) {
- Slog.d(TAG, "SubTitle: " + subTitle);
+ Slog.d(TAG, "SubTitle: " + mSubTitle);
}
final TextView noButton = view.findViewById(R.id.autofill_save_no);
@@ -207,4 +211,18 @@
throw new IllegalStateException("cannot interact with a destroyed instance");
}
}
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("title: "); pw.println(mTitle);
+ pw.print(prefix); pw.print("subtitle: "); pw.println(mSubTitle);
+
+ final View view = mDialog.getWindow().getDecorView();
+ final int[] loc = view.getLocationOnScreen();
+ pw.print(prefix); pw.print("coordinates: ");
+ pw.print('('); pw.print(loc[0]); pw.print(','); pw.print(loc[1]);pw.print(')');
+ pw.print('(');
+ pw.print(loc[0] + view.getWidth()); pw.print(',');
+ pw.print(loc[1] + view.getHeight());pw.println(')');
+ pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+ }
}
diff --git a/services/core/Android.mk b/services/core/Android.mk
index f896478..15493467 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -13,7 +13,6 @@
../../../../system/netd/server/binder/android/net/INetd.aidl \
../../../../system/netd/server/binder/android/net/metrics/INetdEventListener.aidl \
../../../native/cmds/installd/binder/android/os/IInstalld.aidl \
- ../../../native/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl \
LOCAL_AIDL_INCLUDES += \
system/netd/server/binder
diff --git a/services/core/java/com/android/server/SyntheticPasswordManager.java b/services/core/java/com/android/server/SyntheticPasswordManager.java
index 6ec74e1..f797517 100644
--- a/services/core/java/com/android/server/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/SyntheticPasswordManager.java
@@ -346,11 +346,14 @@
PasswordData pwd = PasswordData.create(credentialType);
byte[] pwdToken = computePasswordToken(credential, pwd);
+ // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
+ // to prevent them from accumulating and causing problems.
+ gatekeeper.clearSecureUserId(fakeUid(userId));
GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
passwordTokenToGkInput(pwdToken));
if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
- return 0;
+ return DEFAULT_HANDLE;
}
pwd.passwordHandle = response.getPayload();
long sid = sidFromPasswordHandle(pwd.passwordHandle);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a5615a9..1ed46a0 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -622,6 +622,17 @@
!= ActivityManager.APP_START_MODE_NORMAL) {
if (stopping == null) {
stopping = new ArrayList<>();
+ String compName = service.name.flattenToShortString();
+ EventLogTags.writeAmStopIdleService(service.appInfo.uid, compName);
+ StringBuilder sb = new StringBuilder(64);
+ sb.append("Stopping service due to app idle: ");
+ UserHandle.formatUid(sb, service.appInfo.uid);
+ sb.append(" ");
+ TimeUtils.formatDuration(service.createTime
+ - SystemClock.elapsedRealtime(), sb);
+ sb.append(" ");
+ sb.append(compName);
+ Slog.w(TAG, sb.toString());
stopping.add(service);
}
}
@@ -1364,7 +1375,7 @@
// This could have made the service more important.
mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
|| s.app.treatLikeActivity, b.client);
- mAm.updateOomAdjLocked(s.app);
+ mAm.updateOomAdjLocked(s.app, true);
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b
@@ -1479,13 +1490,15 @@
r.binding.service.app.hasClientActivities
|| r.binding.service.app.treatLikeActivity, null);
}
- mAm.updateOomAdjLocked(r.binding.service.app);
+ mAm.updateOomAdjLocked(r.binding.service.app, false);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
+ mAm.updateOomAdjLocked();
+
return true;
}
@@ -2225,7 +2238,7 @@
bumpServiceExecutingLocked(r, execInFg, "start");
if (!oomAdjusted) {
oomAdjusted = true;
- mAm.updateOomAdjLocked(r.app);
+ mAm.updateOomAdjLocked(r.app, true);
}
if (r.fgRequired && !r.fgWaiting) {
if (!r.isForeground) {
@@ -2349,7 +2362,7 @@
if (ibr.hasBound) {
try {
bumpServiceExecutingLocked(r, false, "bring down unbind");
- mAm.updateOomAdjLocked(r.app);
+ mAm.updateOomAdjLocked(r.app, true);
ibr.hasBound = false;
ibr.requested = false;
r.app.thread.scheduleUnbindService(r,
@@ -2441,7 +2454,7 @@
bumpServiceExecutingLocked(r, false, "destroy");
mDestroyingServices.add(r);
r.destroying = true;
- mAm.updateOomAdjLocked(r.app);
+ mAm.updateOomAdjLocked(r.app, true);
r.app.thread.scheduleStopService(r);
} catch (Exception e) {
Slog.w(TAG, "Exception when destroying service "
@@ -2542,7 +2555,7 @@
// it to go down there and we want it to start out near the top.
mAm.updateLruProcessLocked(s.app, false, null);
}
- mAm.updateOomAdjLocked(s.app);
+ mAm.updateOomAdjLocked(s.app, true);
b.intent.hasBound = false;
// Assume the client doesn't want to know about a rebind;
// we will deal with that later if it asks for one.
@@ -2695,7 +2708,7 @@
mDestroyingServices.remove(r);
r.bindings.clear();
}
- mAm.updateOomAdjLocked(r.app);
+ mAm.updateOomAdjLocked(r.app, true);
}
r.executeFg = false;
if (r.tracker != null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6d69915..b5f8a09 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -812,15 +812,28 @@
final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();
/**
- * All of the processes that have been forced to be foreground. The key
+ * All of the processes that have been forced to be important. The key
* is the pid of the caller who requested it (we hold a death
* link on it).
*/
- abstract class ForegroundToken implements IBinder.DeathRecipient {
- int pid;
- IBinder token;
+ abstract class ImportanceToken implements IBinder.DeathRecipient {
+ final int pid;
+ final IBinder token;
+ final String reason;
+
+ ImportanceToken(int _pid, IBinder _token, String _reason) {
+ pid = _pid;
+ token = _token;
+ reason = _reason;
+ }
+
+ @Override
+ public String toString() {
+ return "ImportanceToken { " + Integer.toHexString(System.identityHashCode(this))
+ + " " + reason + " " + pid + " " + token + " }";
+ }
}
- final SparseArray<ForegroundToken> mForegroundProcesses = new SparseArray<ForegroundToken>();
+ final SparseArray<ImportanceToken> mImportantProcesses = new SparseArray<ImportanceToken>();
/**
* List of records for processes that someone had tried to start before the
@@ -6499,6 +6512,7 @@
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"No more processes in " + old.uidRecord);
enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
+ EventLogTags.writeAmUidStopped(uid);
mActiveUids.remove(uid);
noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
}
@@ -6530,6 +6544,7 @@
}
uidRec.updateHasInternetPermission();
mActiveUids.put(proc.uid, uidRec);
+ EventLogTags.writeAmUidRunning(uidRec.uid);
noteUidProcessState(uidRec.uid, uidRec.curProcState);
enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
}
@@ -6717,7 +6732,7 @@
app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- app.forcingToForeground = null;
+ app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
app.debugging = false;
@@ -6930,7 +6945,7 @@
}
// Check whether the next backup agent is in this process...
- if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.uid) {
+ if (!badApp && mBackupTarget != null && mBackupTarget.app == app) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
"New app is backup target, launching agent for " + app);
notifyPackageUse(mBackupTarget.appInfo.packageName,
@@ -7717,20 +7732,20 @@
}
}
- void foregroundTokenDied(ForegroundToken token) {
+ void importanceTokenDied(ImportanceToken token) {
synchronized (ActivityManagerService.this) {
synchronized (mPidsSelfLocked) {
- ForegroundToken cur
- = mForegroundProcesses.get(token.pid);
+ ImportanceToken cur
+ = mImportantProcesses.get(token.pid);
if (cur != token) {
return;
}
- mForegroundProcesses.remove(token.pid);
+ mImportantProcesses.remove(token.pid);
ProcessRecord pr = mPidsSelfLocked.get(token.pid);
if (pr == null) {
return;
}
- pr.forcingToForeground = null;
+ pr.forcingToImportant = null;
updateProcessForegroundLocked(pr, false, false);
}
updateOomAdjLocked();
@@ -7738,9 +7753,9 @@
}
@Override
- public void setProcessForeground(IBinder token, int pid, boolean isForeground) {
+ public void setProcessImportant(IBinder token, int pid, boolean isForeground, String reason) {
enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
- "setProcessForeground()");
+ "setProcessImportant()");
synchronized(this) {
boolean changed = false;
@@ -7750,28 +7765,26 @@
Slog.w(TAG, "setProcessForeground called on unknown pid: " + pid);
return;
}
- ForegroundToken oldToken = mForegroundProcesses.get(pid);
+ ImportanceToken oldToken = mImportantProcesses.get(pid);
if (oldToken != null) {
oldToken.token.unlinkToDeath(oldToken, 0);
- mForegroundProcesses.remove(pid);
+ mImportantProcesses.remove(pid);
if (pr != null) {
- pr.forcingToForeground = null;
+ pr.forcingToImportant = null;
}
changed = true;
}
if (isForeground && token != null) {
- ForegroundToken newToken = new ForegroundToken() {
+ ImportanceToken newToken = new ImportanceToken(pid, token, reason) {
@Override
public void binderDied() {
- foregroundTokenDied(this);
+ importanceTokenDied(this);
}
};
- newToken.pid = pid;
- newToken.token = token;
try {
token.linkToDeath(newToken, 0);
- mForegroundProcesses.put(pid, newToken);
- pr.forcingToForeground = token;
+ mImportantProcesses.put(pid, newToken);
+ pr.forcingToImportant = newToken;
changed = true;
} catch (RemoteException e) {
// If the process died while doing this, we will later
@@ -11313,7 +11326,7 @@
checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
final int verifiedAdj = cpr.proc.verifiedAdj;
- boolean success = updateOomAdjLocked(cpr.proc);
+ boolean success = updateOomAdjLocked(cpr.proc, true);
// XXX things have changed so updateOomAdjLocked doesn't actually tell us
// if the process has been successfully adjusted. So to reduce races with
// it, we will check whether the process still exists. Note that this doesn't
@@ -11775,7 +11788,7 @@
dst.proc = r;
dst.notifyAll();
}
- updateOomAdjLocked(r);
+ updateOomAdjLocked(r, true);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
@@ -13506,7 +13519,7 @@
}
}
if (changed) {
- updateOomAdjLocked(pr);
+ updateOomAdjLocked(pr, true);
}
}
} finally {
@@ -15490,12 +15503,12 @@
}
}
- if (mForegroundProcesses.size() > 0) {
+ if (mImportantProcesses.size() > 0) {
synchronized (mPidsSelfLocked) {
boolean printed = false;
- for (int i=0; i<mForegroundProcesses.size(); i++) {
+ for (int i = 0; i< mImportantProcesses.size(); i++) {
ProcessRecord r = mPidsSelfLocked.get(
- mForegroundProcesses.valueAt(i).pid);
+ mImportantProcesses.valueAt(i).pid);
if (dumpPackage != null && (r == null
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
@@ -15507,8 +15520,8 @@
printed = true;
printedAnything = true;
}
- pw.print(" PID #"); pw.print(mForegroundProcesses.keyAt(i));
- pw.print(": "); pw.println(mForegroundProcesses.valueAt(i));
+ pw.print(" PID #"); pw.print(mImportantProcesses.keyAt(i));
+ pw.print(": "); pw.println(mImportantProcesses.valueAt(i));
}
}
}
@@ -17779,7 +17792,7 @@
app.unlinkDeathRecipient();
app.makeInactive(mProcessStats);
app.waitingToKill = null;
- app.forcingToForeground = null;
+ app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, false);
app.foregroundActivities = false;
app.hasShownUi = false;
@@ -18279,7 +18292,7 @@
mBackupAppName = app.packageName;
// Try not to kill the process during backup
- updateOomAdjLocked(proc);
+ updateOomAdjLocked(proc, true);
// If the process is already attached, schedule the creation of the backup agent now.
// If it is not yet live, this will be done when it attaches to the framework.
@@ -18376,7 +18389,7 @@
// Not backing this app up any more; reset its OOM adjustment
final ProcessRecord proc = mBackupTarget.app;
- updateOomAdjLocked(proc);
+ updateOomAdjLocked(proc, true);
proc.inFullBackup = false;
oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1;
@@ -20774,14 +20787,6 @@
app.cached = false;
app.adjType = "fg-service";
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- } else if (app.forcingToForeground != null) {
- // The user is aware of this app, so make it visible.
- adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
- app.cached = false;
- app.adjType = "force-fg";
- app.adjSource = app.forcingToForeground;
- schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
} else if (app.hasOverlayUi) {
// The process is display an overlay UI.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
@@ -20792,6 +20797,21 @@
}
}
+ if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
+ || procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+ if (app.forcingToImportant != null) {
+ // This is currently used for toasts... they are not interactive, and
+ // we don't want them to cause the app to become fully foreground (and
+ // thus out of background check), so we yes the best background level we can.
+ adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+ procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+ app.cached = false;
+ app.adjType = "force-imp";
+ app.adjSource = app.forcingToImportant;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ }
+ }
+
if (app == mHeavyWeightProcess) {
if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
// We don't want to kill the current heavy-weight process.
@@ -22032,10 +22052,7 @@
+ mConstants.SERVICE_USAGE_INTERACTION_TIME;
}
} else {
- // If the app was being forced to the foreground, by say a Toast, then
- // no need to treat it as an interaction
- isInteraction = app.forcingToForeground == null
- && app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ isInteraction = app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
app.fgInteractionTime = 0;
}
if (isInteraction && (!app.reportedInteraction || (nowElapsed-app.interactionEventTime)
@@ -22136,7 +22153,14 @@
return act;
}
- final boolean updateOomAdjLocked(ProcessRecord app) {
+ /**
+ * Update OomAdj for a specific process.
+ * @param app The process to update
+ * @param oomAdjAll If it's ok to call updateOomAdjLocked() for all running apps
+ * if necessary, or skip.
+ * @return whether updateOomAdjLocked(app) was successful.
+ */
+ final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
final boolean wasCached = app.cached;
@@ -22151,7 +22175,8 @@
? app.curRawAdj : ProcessList.UNKNOWN_ADJ;
boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false,
SystemClock.uptimeMillis());
- if (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ) {
+ if (oomAdjAll
+ && (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ)) {
// Changed to/from cached state, so apps after it in the LRU
// list may also be changed.
updateOomAdjLocked();
@@ -22565,6 +22590,7 @@
} else {
if (uidRec.idle) {
uidChange = UidRecord.CHANGE_ACTIVE;
+ EventLogTags.writeAmUidActive(uidRec.uid);
uidRec.idle = false;
}
uidRec.lastBackgroundTime = 0;
@@ -22643,6 +22669,7 @@
if (UserHandle.getAppId(uidRec.uid) == appId) {
if (userId == UserHandle.USER_ALL ||
userId == UserHandle.getUserId(uidRec.uid)) {
+ EventLogTags.writeAmUidIdle(uidRec.uid);
uidRec.idle = true;
Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uidRec.uid)
+ " from package " + packageName + " user " + userId);
@@ -22677,6 +22704,7 @@
final long bgTime = uidRec.lastBackgroundTime;
if (bgTime > 0 && !uidRec.idle) {
if (bgTime <= maxBgTime) {
+ EventLogTags.writeAmUidIdle(uidRec.uid);
uidRec.idle = true;
doStopUidLocked(uidRec.uid, uidRec);
} else {
@@ -23729,7 +23757,7 @@
}
pr.hasOverlayUi = hasOverlayUi;
//Slog.i(TAG, "Setting hasOverlayUi=" + pr.hasOverlayUi + " for pid=" + pid);
- updateOomAdjLocked(pr);
+ updateOomAdjLocked(pr, true);
}
}
@@ -23867,6 +23895,34 @@
}
}
+ public void waitForBroadcastIdle(PrintWriter pw) {
+ enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()");
+ while (true) {
+ boolean idle = true;
+ synchronized (this) {
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ if (!queue.isIdle()) {
+ final String msg = "Waiting for queue " + queue + " to become idle...";
+ pw.println(msg);
+ pw.flush();
+ Slog.v(TAG, msg);
+ idle = false;
+ }
+ }
+ }
+
+ if (idle) {
+ final String msg = "All broadcast queues are idle!";
+ pw.println(msg);
+ pw.flush();
+ Slog.v(TAG, msg);
+ return;
+ } else {
+ SystemClock.sleep(1000);
+ }
+ }
+ }
+
/**
* Return the user id of the last resumed activity.
*/
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index b6bfb00..dab122f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -251,6 +251,8 @@
return runUpdateApplicationInfo(pw);
case "no-home-screen":
return runNoHomeScreen(pw);
+ case "wait-for-broadcast-idle":
+ return runWaitForBroadcastIdle(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -2419,6 +2421,11 @@
return 0;
}
+ int runWaitForBroadcastIdle(PrintWriter pw) throws RemoteException {
+ mInternal.waitForBroadcastIdle(pw);
+ return 0;
+ }
+
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index ba2c0cd..01fbdd4 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -249,6 +249,8 @@
// Last configuration reported to the activity in the client process.
private MergedConfiguration mLastReportedConfiguration;
private int mLastReportedDisplayId;
+ private boolean mLastReportedMultiWindowMode;
+ private boolean mLastReportedPictureInPictureMode;
CompatibilityInfo compat;// last used compatibility mode
ActivityRecord resultTo; // who started this entry, so will get our reply
final String resultWho; // additional identifier for use by resultTo.
@@ -289,10 +291,6 @@
boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
boolean immersive; // immersive mode (don't interrupt if possible)
boolean forceNewConfig; // force re-create with new config next time
- private boolean mInMultiWindowMode; // whether or not this activity is currently in multi-window
- // mode (default false)
- private boolean mInPictureInPictureMode; // whether or not this activity is currently in
- // picture-in-picture mode (default false)
boolean supportsPictureInPictureWhilePausing; // This flag is set by the system to indicate
// that the activity can enter picture in picture while pausing (ie. only when another
// task is brought to front or started)
@@ -535,6 +533,8 @@
}
if (info != null) {
pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
+ pw.println(prefix + "mLastReportedMultiWindowMode=" + mLastReportedMultiWindowMode
+ + " mLastReportedPictureInPictureMode=" + mLastReportedPictureInPictureMode);
if (info.supportsPictureInPicture()) {
pw.println(prefix + "supportsPictureInPicture=" + info.supportsPictureInPicture());
pw.println(prefix + "supportsPictureInPictureWhilePausing: "
@@ -637,15 +637,15 @@
// An activity is considered to be in multi-window mode if its task isn't fullscreen.
final boolean inMultiWindowMode = !task.mFullscreen;
- if (inMultiWindowMode != mInMultiWindowMode) {
- mInMultiWindowMode = inMultiWindowMode;
+ if (inMultiWindowMode != mLastReportedMultiWindowMode) {
+ mLastReportedMultiWindowMode = inMultiWindowMode;
scheduleMultiWindowModeChanged(getConfiguration());
}
}
private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
try {
- app.thread.scheduleMultiWindowModeChanged(appToken, mInMultiWindowMode,
+ app.thread.scheduleMultiWindowModeChanged(appToken, mLastReportedMultiWindowMode,
overrideConfig);
} catch (Exception e) {
// If process died, I don't care.
@@ -659,11 +659,11 @@
final boolean inPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID) &&
(targetStackBounds != null);
- if (inPictureInPictureMode != mInPictureInPictureMode) {
+ if (inPictureInPictureMode != mLastReportedPictureInPictureMode) {
// Picture-in-picture mode changes also trigger a multi-window mode change as well, so
// update that here in order
- mInPictureInPictureMode = inPictureInPictureMode;
- mInMultiWindowMode = inPictureInPictureMode;
+ mLastReportedPictureInPictureMode = inPictureInPictureMode;
+ mLastReportedMultiWindowMode = inPictureInPictureMode;
final Configuration newConfig = task.computeNewOverrideConfigurationForBounds(
targetStackBounds, null);
schedulePictureInPictureModeChanged(newConfig);
@@ -673,7 +673,8 @@
private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
try {
- app.thread.schedulePictureInPictureModeChanged(appToken, mInPictureInPictureMode,
+ app.thread.schedulePictureInPictureModeChanged(appToken,
+ mLastReportedPictureInPictureMode,
overrideConfig);
} catch (Exception e) {
// If process died, no one cares.
@@ -940,6 +941,12 @@
task.addActivityToTop(this);
+ // When an activity is started directly into a split-screen fullscreen stack, we need to
+ // update the initial multi-window modes so that the callbacks are scheduled correctly when
+ // the user leaves that mode.
+ mLastReportedMultiWindowMode = !task.mFullscreen;
+ mLastReportedPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID);
+
onOverrideConfigurationSent();
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index d348224..c5d5867 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -232,7 +232,7 @@
static final int STACK_VISIBLE = 1;
// Stack is considered visible, but only becuase it has activity that is visible behind other
// activities and there is a specific combination of stacks.
- private static final int STACK_VISIBLE_ACTIVITY_BEHIND = 2;
+ static final int STACK_VISIBLE_ACTIVITY_BEHIND = 2;
@VisibleForTesting
/* The various modes for the method {@link #removeTask}. */
@@ -1652,7 +1652,7 @@
if (StackId.isBackdropToTranslucentActivity(mStackId)
&& hasVisibleBehindActivity() && StackId.isHomeOrRecentsStack(topStackId)
- && !topStack.topActivity().fullscreen) {
+ && (topStack.topActivity() == null || !topStack.topActivity().fullscreen)) {
// The fullscreen or assistant stack should be visible if it has a visible behind
// activity behind the home or recents stack that is translucent.
return STACK_VISIBLE_ACTIVITY_BEHIND;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 6d6f33d..e33ae0d 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -577,7 +577,7 @@
}
boolean clearedTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
- == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+ == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK) && (mReuseTask != null);
if (startedActivityStackId == PINNED_STACK_ID && (result == START_TASK_TO_FRONT
|| result == START_DELIVERED_TO_TOP || clearedTask)) {
// The activity was already running in the pinned stack so it wasn't started, but either
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index d6bfb35..cfb5478 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -740,6 +740,8 @@
}
// If we've created a crash dialog, show it without the lock held
if(data.proc.crashDialog != null) {
+ Slog.i(TAG, "Showing crash dialog for package " + data.proc.info.packageName
+ + " u" + data.proc.userId);
data.proc.crashDialog.show();
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 983c975..7a46248 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -966,6 +966,14 @@
}
@Override
+ public void noteBleScanResult(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteBluetoothScanResultFromSourceLocked(ws);
+ }
+ }
+
+ @Override
public void noteWifiControllerActivity(WifiActivityEnergyInfo info) {
enforceCallingPermission();
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index d08298b..639b7a9 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -51,7 +51,6 @@
import android.util.EventLog;
import android.util.Slog;
import android.util.TimeUtils;
-import com.android.server.DeviceIdleController;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -204,6 +203,11 @@
mDelayBehindServices = allowDelayBehindServices;
}
+ @Override
+ public String toString() {
+ return mQueueName;
+ }
+
public boolean isPendingBroadcastProcessLocked(int pid) {
return mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid;
}
@@ -682,7 +686,7 @@
// are already core system stuff so don't matter for this.
r.curApp = filter.receiverList.app;
filter.receiverList.app.curReceivers.add(r);
- mService.updateOomAdjLocked(r.curApp);
+ mService.updateOomAdjLocked(r.curApp, true);
}
}
try {
@@ -1579,6 +1583,11 @@
record.intent == null ? "" : record.intent.getAction());
}
+ final boolean isIdle() {
+ return mParallelBroadcasts.isEmpty() && mOrderedBroadcasts.isEmpty()
+ && (mPendingBroadcast == null);
+ }
+
final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index f618fc7..372ab6b 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -114,3 +114,14 @@
# UserState has changed
30051 am_user_state_changed (id|1|5),(state|1|5)
+
+# Note when any processes of a uid have started running
+30052 am_uid_running (UID|1|5)
+# Note when all processes of a uid have stopped.
+30053 am_uid_stopped (UID|1|5)
+# Note when the state of a uid has become active.
+30054 am_uid_active (UID|1|5)
+# Note when the state of a uid has become idle (background check enforced).
+30055 am_uid_idle (UID|1|5)
+# Note when a service is being forcibly stopped because its app went idle.
+30056 am_stop_idle_service (UID|1|5),(Component Name|3)
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index fbc2bd2..b222e3a 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -135,7 +135,7 @@
long interactionEventTime; // The time we sent the last interaction event
long fgInteractionTime; // When we became foreground for interaction purposes
String waitingToKill; // Process is waiting to be killed when in the bg, and reason
- IBinder forcingToForeground;// Token that is forcing this process to be foreground
+ Object forcingToImportant; // Token that is forcing this process to be important
int adjSeq; // Sequence id for identifying oom_adj assignment cycles
int lruSeq; // Sequence id for identifying LRU update cycles
CompatibilityInfo compat; // last used compatibility mode
@@ -302,9 +302,9 @@
pw.print(" hasAboveClient="); pw.print(hasAboveClient);
pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
}
- if (foregroundServices || forcingToForeground != null) {
+ if (foregroundServices || forcingToImportant != null) {
pw.print(prefix); pw.print("foregroundServices="); pw.print(foregroundServices);
- pw.print(" forcingToForeground="); pw.println(forcingToForeground);
+ pw.print(" forcingToImportant="); pw.println(forcingToImportant);
}
if (reportedInteraction || fgInteractionTime != 0) {
pw.print(prefix); pw.print("reportedInteraction=");
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 3b5e5bc..6a310f2 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -439,7 +439,7 @@
}
}
- Slog.d(TAG, "Sending BOOT_COMPLETE user #" + userId);
+ Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId);
// Do not report secondary users, runtime restarts or first boot/upgrade
if (userId == UserHandle.USER_SYSTEM
&& !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
@@ -451,7 +451,14 @@
bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mInjector.broadcastIntentLocked(bootIntent, null, null, 0, null, null,
+ mInjector.broadcastIntentLocked(bootIntent, null, new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser)
+ throws RemoteException {
+ Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId);
+ }
+ }, 0, null, null,
new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 73cbffd..125095c 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -322,8 +322,8 @@
if (mFocusLossWasNotified) {
fd.dispatchAudioFocusChange(focusGain, mClientId);
}
- mFocusController.unduckPlayers(this);
}
+ mFocusController.unduckPlayers(this);
mFocusLossWasNotified = false;
} catch (android.os.RemoteException e) {
Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
@@ -333,6 +333,15 @@
/**
* Called synchronized on MediaFocusControl.mAudioFocusLock
*/
+ void handleFocusGainFromRequest(int focusRequestResult) {
+ if (focusRequestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ mFocusController.unduckPlayers(this);
+ }
+ }
+
+ /**
+ * Called synchronized on MediaFocusControl.mAudioFocusLock
+ */
void handleFocusLoss(int focusLoss, @Nullable final FocusRequester fr) {
try {
if (focusLoss != mFocusLossReceived) {
@@ -381,6 +390,8 @@
Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
+ " to " + mClientId + ", ducking implemented by framework");
}
+ mFocusController.notifyExtPolicyFocusLoss_syncAf(
+ toAudioFocusInfo(), false /* wasDispatched */);
return; // with mFocusLossWasNotified = false
}
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index f5c13c1..7d742ff 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -752,6 +752,7 @@
// push focus requester at the top of the audio focus stack
mFocusStack.push(nfr);
+ nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
}
notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java
index 7849896..d574265 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/NightDisplayService.java
@@ -567,22 +567,27 @@
private final TwilightManager mTwilightManager;
- private Calendar mLastActivatedTime;
-
TwilightAutoMode() {
mTwilightManager = getLocalService(TwilightManager.class);
}
private void updateActivated(TwilightState state) {
- boolean activate = state != null && state.isNight();
- if (state != null && mLastActivatedTime != null) {
+ if (state == null) {
+ // If there isn't a valid TwilightState then just keep the current activated
+ // state.
+ return;
+ }
+
+ boolean activate = state.isNight();
+ final Calendar lastActivatedTime = getLastActivatedTime();
+ if (lastActivatedTime != null) {
final Calendar now = Calendar.getInstance();
final Calendar sunrise = state.sunrise();
final Calendar sunset = state.sunset();
// Maintain the existing activated state if within the current period.
- if (mLastActivatedTime.before(now)
- && (mLastActivatedTime.after(sunrise) ^ mLastActivatedTime.after(sunset))) {
+ if (lastActivatedTime.before(now)
+ && (lastActivatedTime.after(sunrise) ^ lastActivatedTime.after(sunset))) {
activate = mController.isActivated();
}
}
@@ -595,7 +600,6 @@
@Override
public void onStart() {
mTwilightManager.registerListener(this, mHandler);
- mLastActivatedTime = getLastActivatedTime();
// Force an update to initialize state.
updateActivated(mTwilightManager.getLastTwilightState());
@@ -604,14 +608,10 @@
@Override
public void onStop() {
mTwilightManager.unregisterListener(this);
- mLastActivatedTime = null;
}
@Override
public void onActivated(boolean activated) {
- if (mIsActivated != null) {
- mLastActivatedTime = getLastActivatedTime();
- }
}
@Override
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 9b984c1..6f5b028 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -16,21 +16,20 @@
package com.android.server.fingerprint;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.USE_FINGERPRINT;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
-import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.SynchronousUserSwitchObserver;
-import android.content.ComponentName;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -82,10 +81,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.LinkedList;
/**
* A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -400,6 +399,10 @@
ClientMonitor client = mCurrentClient;
if (client != null && client.onRemoved(fingerId, groupId, remaining)) {
removeClient(client);
+ // When the last fingerprint of a group is removed, update the authenticator id
+ if (!hasEnrolledFingerprints(groupId)) {
+ updateActiveGroup(groupId, null);
+ }
}
if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
cleanupUnknownFingerprints();
@@ -446,6 +449,9 @@
ClientMonitor client = mCurrentClient;
if (client != null && client.onEnrollResult(fingerId, groupId, remaining)) {
removeClient(client);
+ // When enrollment finishes, update this group's authenticator id, as the HAL has
+ // already generated a new authenticator id when the new fingerprint is enrolled.
+ updateActiveGroup(groupId, null);
}
}
@@ -1382,7 +1388,8 @@
daemon.setActiveGroup(userId, fpDir.getAbsolutePath());
mCurrentUserId = userId;
}
- mAuthenticatorIds.put(userId, daemon.getAuthenticatorId());
+ mAuthenticatorIds.put(userId,
+ hasEnrolledFingerprints(userId) ? daemon.getAuthenticatorId() : 0L);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveGroup():", e);
}
@@ -1436,7 +1443,6 @@
*/
public long getAuthenticatorId(String opPackageName) {
final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
- Long authenticatorId = mAuthenticatorIds.get(userId);
- return authenticatorId != null ? authenticatorId : 0;
+ return mAuthenticatorIds.getOrDefault(userId, 0L);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1d843c1..70766f9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2871,8 +2871,7 @@
adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
System.currentTimeMillis());
summaryRecord = new NotificationRecord(getContext(), summarySbn,
- notificationRecord.getChannel(), mRankingHelper.supportsChannels(
- summarySbn.getPackageName(), summarySbn.getUid()));
+ notificationRecord.getChannel());
summaries.put(pkg, summarySbn.getKey());
}
}
@@ -3211,8 +3210,7 @@
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, notificationUid, callingPid, notification,
user, null, System.currentTimeMillis());
- final NotificationRecord r = new NotificationRecord(getContext(), n, channel,
- mRankingHelper.supportsChannels(pkg, notificationUid));
+ final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
if (!checkDisqualifyingFeatures(userId, notificationUid, id,tag, r)) {
return;
@@ -3937,7 +3935,7 @@
}
}
try {
- mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
+ mAm.setProcessImportant(mForegroundToken, pid, toastCount > 0, "toast");
} catch (RemoteException e) {
// Shouldn't happen.
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f019a5c..5a5e658 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -116,7 +116,7 @@
private int mSuppressedVisualEffects = 0;
private String mUserExplanation;
private String mPeopleExplanation;
- private boolean mSupportsChannels = false;
+ private boolean mPreChannelsNotification = true;
private Uri mSound;
private long[] mVibration;
private AudioAttributes mAttributes;
@@ -129,7 +129,7 @@
@VisibleForTesting
public NotificationRecord(Context context, StatusBarNotification sbn,
- NotificationChannel channel, boolean supportsChannels)
+ NotificationChannel channel)
{
this.sbn = sbn;
mOriginalFlags = sbn.getNotification().flags;
@@ -139,7 +139,7 @@
mContext = context;
stats = new NotificationUsageStats.SingleNotificationStats();
mChannel = channel;
- mSupportsChannels = supportsChannels;
+ mPreChannelsNotification = isPreChannelsNotification();
mSound = calculateSound();
mVibration = calculateVibration();
mAttributes = calculateAttributes();
@@ -147,11 +147,27 @@
mLight = calculateLights();
}
+ private boolean isPreChannelsNotification() {
+ try {
+ if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
+ final ApplicationInfo applicationInfo =
+ mContext.getPackageManager().getApplicationInfoAsUser(sbn.getPackageName(),
+ 0, UserHandle.getUserId(sbn.getUid()));
+ if (applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
+ return true;
+ }
+ }
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Can't find package", e);
+ }
+ return false;
+ }
+
private Uri calculateSound() {
final Notification n = sbn.getNotification();
Uri sound = mChannel.getSound();
- if (!mSupportsChannels && (getChannel().getUserLockedFields()
+ if (mPreChannelsNotification && (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_SOUND) == 0) {
final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;
@@ -176,7 +192,7 @@
: defaultLightColor;
Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,
defaultLightOn, defaultLightOff) : null;
- if (!mSupportsChannels
+ if (mPreChannelsNotification
&& (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
final Notification notification = sbn.getNotification();
@@ -207,7 +223,7 @@
} else {
vibration = null;
}
- if (!mSupportsChannels
+ if (mPreChannelsNotification
&& (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
final Notification notification = sbn.getNotification();
@@ -229,7 +245,7 @@
attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
}
- if (!mSupportsChannels
+ if (mPreChannelsNotification
&& (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_SOUND) == 0) {
if (n.audioAttributes != null) {
@@ -278,7 +294,7 @@
stats.requestedImportance = requestedImportance;
stats.isNoisy = mSound != null || mVibration != null;
- if (!mSupportsChannels
+ if (mPreChannelsNotification
&& (importance == IMPORTANCE_UNSPECIFIED
|| (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_IMPORTANCE) == 0)) {
@@ -445,7 +461,7 @@
pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
- if (!mSupportsChannels) {
+ if (mPreChannelsNotification) {
pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
notification.defaults, notification.flags));
pw.println(prefix + "n.sound=" + notification.sound);
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 14d796f..4d19b52 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -42,6 +42,4 @@
void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannels(String pkg, int uid);
ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, boolean includeDeleted);
-
- boolean supportsChannels(String pkg, int uid);
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 1e741de..7758516 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -273,14 +273,8 @@
}
private boolean shouldHaveDefaultChannel(Record r) throws NameNotFoundException {
- if (supportsChannels(r)) {
- return false;
- }
-
final int userId = UserHandle.getUserId(r.uid);
- final ApplicationInfo applicationInfo = mPm.getApplicationInfoAsUser(r.pkg,
- PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
+ final ApplicationInfo applicationInfo = mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
if (applicationInfo.targetSdkVersion > Build.VERSION_CODES.N_MR1) {
// O apps should not have the default channel.
return false;
@@ -506,31 +500,6 @@
}
@Override
- public boolean supportsChannels(String pkg, int uid) {
- Record r = getOrCreateRecord(pkg, uid);
-
- if (r == null) {
- return false;
- }
-
- if (r.channels.size() == 1
- && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- return false;
- }
-
- return true;
- }
-
- private boolean supportsChannels(Record r) {
- if (r.channels.size() == 1
- && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- return false;
- }
-
- return (r.channels.size() > 0);
- }
-
- @Override
public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
boolean fromTargetApp) {
Preconditions.checkNotNull(pkg);
@@ -602,10 +571,6 @@
r.channels.put(channel.getId(), channel);
MetricsLogger.action(getChannelLog(channel, pkg).setType(
MetricsProto.MetricsEvent.TYPE_OPEN));
-
- // Remove Default Channel.
- r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
-
updateConfig();
}
@@ -702,7 +667,13 @@
if (r == null) {
return;
}
- r.channels.clear();
+ int N = r.channels.size() - 1;
+ for (int i = N; i >= 0; i--) {
+ String key = r.channels.keyAt(i);
+ if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
+ r.channels.remove(key);
+ }
+ }
updateConfig();
}
@@ -1066,8 +1037,6 @@
final int uid = uidList[i];
synchronized (mRecords) {
mRecords.remove(recordKey(pkg, uid));
- // reset to default settings and re-add misc channel for pre-O apps
- getOrCreateRecord(pkg, uid);
}
mRestoredWithoutUids.remove(pkg);
updated = true;
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index 09e9433..aa780cc 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -80,19 +80,23 @@
}
public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(int hashPrefix[],
- String token) {
+ String token) throws ConnectionException {
throwIfCalledOnMainThread();
IInstantAppResolver target = null;
try {
- target = getRemoteInstanceLazy(token);
- return mGetEphemeralResolveInfoCaller
- .getEphemeralResolveInfoList(target, hashPrefix, token);
- } catch (RemoteException e) {
- } catch (InterruptedException | TimeoutException e) {
- if (target == null) {
- Slog.w(TAG, "[" + token + "] Timeout! Phase1 binding to instant app resolver");
- } else {
- Slog.w(TAG, "[" + token + "] Timeout! Phase1 resolving instant app");
+ try {
+ target = getRemoteInstanceLazy(token);
+ } catch (TimeoutException e) {
+ throw new ConnectionException(ConnectionException.FAILURE_BIND);
+ } catch (InterruptedException e) {
+ throw new ConnectionException(ConnectionException.FAILURE_INTERRUPTED);
+ }
+ try {
+ return mGetEphemeralResolveInfoCaller
+ .getEphemeralResolveInfoList(target, hashPrefix, token);
+ } catch (TimeoutException e) {
+ throw new ConnectionException(ConnectionException.FAILURE_BIND);
+ } catch (RemoteException ignore) {
}
} finally {
synchronized (mLock) {
@@ -104,7 +108,7 @@
public final void getInstantAppIntentFilterList(int hashPrefix[], String token,
String hostName, PhaseTwoCallback callback, Handler callbackHandler,
- final long startTime) {
+ final long startTime) throws ConnectionException {
final IRemoteCallback remoteCallback = new IRemoteCallback.Stub() {
@Override
public void sendResult(Bundle data) throws RemoteException {
@@ -122,14 +126,16 @@
try {
getRemoteInstanceLazy(token)
.getInstantAppIntentFilterList(hashPrefix, token, hostName, remoteCallback);
- } catch (RemoteException e) {
- } catch (InterruptedException | TimeoutException e) {
- Slog.w(TAG, "[" + token + "] Timeout! Phase2 binding to instant app resolver");
+ } catch (TimeoutException e) {
+ throw new ConnectionException(ConnectionException.FAILURE_BIND);
+ } catch (InterruptedException e) {
+ throw new ConnectionException(ConnectionException.FAILURE_INTERRUPTED);
+ } catch (RemoteException ignore) {
}
}
private IInstantAppResolver getRemoteInstanceLazy(String token)
- throws TimeoutException, InterruptedException {
+ throws ConnectionException, TimeoutException, InterruptedException {
synchronized (mLock) {
if (mRemoteInstance != null) {
return mRemoteInstance;
@@ -139,7 +145,7 @@
}
}
- private void waitForBind(String token) throws TimeoutException, InterruptedException {
+ private void waitForBindLocked(String token) throws TimeoutException, InterruptedException {
final long startMillis = SystemClock.uptimeMillis();
while (mIsBinding) {
if (mRemoteInstance != null) {
@@ -154,12 +160,13 @@
}
}
- private void bindLocked(String token) throws TimeoutException, InterruptedException {
+ private void bindLocked(String token)
+ throws ConnectionException, TimeoutException, InterruptedException {
if (DEBUG_EPHEMERAL && mIsBinding && mRemoteInstance == null) {
Slog.i(TAG, "[" + token + "] Previous bind timed out; waiting for connection");
}
try {
- waitForBind(token);
+ waitForBindLocked(token);
} catch (TimeoutException e) {
if (DEBUG_EPHEMERAL) {
Slog.i(TAG, "[" + token + "] Previous connection never established; rebinding");
@@ -179,9 +186,10 @@
wasBound = mContext
.bindServiceAsUser(mIntent, mServiceConnection, flags, UserHandle.SYSTEM);
if (wasBound) {
- waitForBind(token);
+ waitForBindLocked(token);
} else {
Slog.w(TAG, "[" + token + "] Failed to bind to: " + mIntent);
+ throw new ConnectionException(ConnectionException.FAILURE_BIND);
}
} finally {
mIsBinding = wasBound && mRemoteInstance == null;
@@ -222,6 +230,17 @@
List<InstantAppResolveInfo> instantAppResolveInfoList, long startTime);
}
+ public static class ConnectionException extends Exception {
+ public static final int FAILURE_BIND = 1;
+ public static final int FAILURE_CALL = 2;
+ public static final int FAILURE_INTERRUPTED = 3;
+
+ public final int failure;
+ public ConnectionException(int _failure) {
+ failure = _failure;
+ }
+ }
+
private final class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index f3c9cbb..85be4a2 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -22,6 +22,7 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_RESOLUTION_DELAY_MS;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_RESOLUTION_STATUS;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -50,20 +51,37 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.pm.EphemeralResolverConnection.ConnectionException;
import com.android.server.pm.EphemeralResolverConnection.PhaseTwoCallback;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
+import java.util.concurrent.TimeoutException;
/** @hide */
public abstract class InstantAppResolver {
private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
private static final String TAG = "PackageManager";
- private static int RESOLUTION_SUCCESS = 0;
- private static int RESOLUTION_FAILURE = 1;
+ private static final int RESOLUTION_SUCCESS = 0;
+ private static final int RESOLUTION_FAILURE = 1;
+ /** Binding to the external service timed out */
+ private static final int RESOLUTION_BIND_TIMEOUT = 2;
+ /** The call to retrieve an instant application response timed out */
+ private static final int RESOLUTION_CALL_TIMEOUT = 3;
+
+ @IntDef(flag = true, prefix = { "RESOLUTION_" }, value = {
+ RESOLUTION_SUCCESS,
+ RESOLUTION_FAILURE,
+ RESOLUTION_BIND_TIMEOUT,
+ RESOLUTION_CALL_TIMEOUT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ResolutionStatus {}
private static MetricsLogger sMetricsLogger;
private static MetricsLogger getLogger() {
@@ -78,29 +96,43 @@
final long startTime = System.currentTimeMillis();
final String token = UUID.randomUUID().toString();
if (DEBUG_EPHEMERAL) {
- Log.d(TAG, "[" + token + "] Resolving phase 1");
+ Log.d(TAG, "[" + token + "] Phase1; resolving");
}
final Intent intent = requestObj.origIntent;
final InstantAppDigest digest =
new InstantAppDigest(intent.getData().getHost(), 5 /*maxDigests*/);
final int[] shaPrefix = digest.getDigestPrefix();
- final List<InstantAppResolveInfo> instantAppResolveInfoList =
- connection.getInstantAppResolveInfoList(shaPrefix, token);
-
- if (instantAppResolveInfoList == null || instantAppResolveInfoList.size() == 0) {
- // No hash prefix match; there are no instant apps for this domain.
- if (DEBUG_EPHEMERAL) {
- Log.d(TAG, "[" + token + "] No results returned");
+ AuxiliaryResolveInfo resolveInfo = null;
+ @ResolutionStatus int resolutionStatus = RESOLUTION_SUCCESS;
+ try {
+ final List<InstantAppResolveInfo> instantAppResolveInfoList =
+ connection.getInstantAppResolveInfoList(shaPrefix, token);
+ if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
+ resolveInfo = InstantAppResolver.filterInstantAppIntent(
+ instantAppResolveInfoList, intent, requestObj.resolvedType,
+ requestObj.userId, intent.getPackage(), digest, token);
}
- return null;
+ } catch (ConnectionException e) {
+ if (e.failure == ConnectionException.FAILURE_BIND) {
+ resolutionStatus = RESOLUTION_BIND_TIMEOUT;
+ } else if (e.failure == ConnectionException.FAILURE_CALL) {
+ resolutionStatus = RESOLUTION_CALL_TIMEOUT;
+ } else {
+ resolutionStatus = RESOLUTION_FAILURE;
+ }
}
- final AuxiliaryResolveInfo resolveInfo = InstantAppResolver.filterInstantAppIntent(
- instantAppResolveInfoList, intent, requestObj.resolvedType, requestObj.userId,
- intent.getPackage(), digest, token);
logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE, startTime, token,
- RESOLUTION_SUCCESS);
+ resolutionStatus);
if (DEBUG_EPHEMERAL && resolveInfo == null) {
- Log.d(TAG, "[" + token + "] No results matched");
+ if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) {
+ Log.d(TAG, "[" + token + "] Phase1; bind timed out");
+ } else if (resolutionStatus == RESOLUTION_CALL_TIMEOUT) {
+ Log.d(TAG, "[" + token + "] Phase1; call timed out");
+ } else if (resolutionStatus != RESOLUTION_SUCCESS) {
+ Log.d(TAG, "[" + token + "] Phase1; service connection error");
+ } else {
+ Log.d(TAG, "[" + token + "] Phase1; No results matched");
+ }
}
return resolveInfo;
}
@@ -111,7 +143,7 @@
final long startTime = System.currentTimeMillis();
final String token = requestObj.responseObj.token;
if (DEBUG_EPHEMERAL) {
- Log.d(TAG, "[" + token + "] Resolving phase 2");
+ Log.d(TAG, "[" + token + "] Phase2; resolving");
}
final Intent intent = requestObj.origIntent;
final String hostName = intent.getData().getHost();
@@ -170,8 +202,24 @@
context.startActivity(installerIntent);
}
};
- connection.getInstantAppIntentFilterList(
- shaPrefix, token, hostName, callback, callbackHandler, startTime);
+ try {
+ connection.getInstantAppIntentFilterList(
+ shaPrefix, token, hostName, callback, callbackHandler, startTime);
+ } catch (ConnectionException e) {
+ @ResolutionStatus int resolutionStatus = RESOLUTION_FAILURE;
+ if (e.failure == ConnectionException.FAILURE_BIND) {
+ resolutionStatus = RESOLUTION_BIND_TIMEOUT;
+ }
+ logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token,
+ resolutionStatus);
+ if (DEBUG_EPHEMERAL) {
+ if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) {
+ Log.d(TAG, "[" + token + "] Phase2; bind timed out");
+ } else {
+ Log.d(TAG, "[" + token + "] Phase2; service connection error");
+ }
+ }
+ }
}
/**
@@ -322,7 +370,8 @@
return null;
}
- private static void logMetrics(int action, long startTime, String token, int status) {
+ private static void logMetrics(int action, long startTime, String token,
+ @ResolutionStatus int status) {
final LogMaker logMaker = new LogMaker(action)
.setType(MetricsProto.MetricsEvent.TYPE_ACTION)
.addTaggedData(FIELD_INSTANT_APP_RESOLUTION_DELAY_MS,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 55285c8..0173533 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6498,11 +6498,12 @@
String resolvedType, int flags, int userId) {
// first, check to see if we've got an instant app already installed
final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
- boolean localInstantAppAvailable = false;
+ ResolveInfo localInstantApp = null;
boolean blockResolution = false;
if (!alreadyResolvedLocally) {
final List<ResolveInfo> instantApps = mActivities.queryIntent(intent, resolvedType,
flags
+ | PackageManager.GET_RESOLVED_FILTER
| PackageManager.MATCH_INSTANT
| PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY,
userId);
@@ -6529,7 +6530,7 @@
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Found installed instant app; pkg: " + packageName);
}
- localInstantAppAvailable = true;
+ localInstantApp = info;
break;
}
}
@@ -6537,17 +6538,29 @@
}
// no app installed, let's see if one's available
AuxiliaryResolveInfo auxiliaryResponse = null;
- if (!localInstantAppAvailable && !blockResolution) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
- final InstantAppRequest requestObject = new InstantAppRequest(
- null /*responseObj*/, intent /*origIntent*/, resolvedType,
- null /*callingPackage*/, userId, null /*verificationBundle*/);
- auxiliaryResponse =
- InstantAppResolver.doInstantAppResolutionPhaseOne(
- mContext, mInstantAppResolverConnection, requestObject);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (!blockResolution) {
+ if (localInstantApp == null) {
+ // we don't have an instant app locally, resolve externally
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
+ final InstantAppRequest requestObject = new InstantAppRequest(
+ null /*responseObj*/, intent /*origIntent*/, resolvedType,
+ null /*callingPackage*/, userId, null /*verificationBundle*/);
+ auxiliaryResponse =
+ InstantAppResolver.doInstantAppResolutionPhaseOne(
+ mContext, mInstantAppResolverConnection, requestObject);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ } else {
+ // we have an instant application locally, but, we can't admit that since
+ // callers shouldn't be able to determine prior browsing. create a dummy
+ // auxiliary response so the downstream code behaves as if there's an
+ // instant application available externally. when it comes time to start
+ // the instant application, we'll do the right thing.
+ final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo;
+ auxiliaryResponse = new AuxiliaryResolveInfo(
+ ai.packageName, null /*splitName*/, ai.versionCode, null /*failureIntent*/);
+ }
}
- if (localInstantAppAvailable || auxiliaryResponse != null) {
+ if (auxiliaryResponse != null) {
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
@@ -6567,7 +6580,7 @@
ephemeralInstaller.filter = new IntentFilter(intent.getAction());
ephemeralInstaller.filter.addDataPath(
intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
- ephemeralInstaller.instantAppAvailable = true;
+ ephemeralInstaller.isInstantAppAvailable = true;
result.add(ephemeralInstaller);
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index bed8f1a..7f0528a 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1611,6 +1611,11 @@
*/
private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate,
boolean forPinRequest) {
+ if (shortcut.isReturnedByServer()) {
+ Log.w(TAG,
+ "Re-publishing ShortcutInfo returned by server is not supported."
+ + " Some information such as icon may lost from shortcut.");
+ }
Preconditions.checkNotNull(shortcut, "Null shortcut detected");
if (shortcut.getActivity() != null) {
Preconditions.checkState(
@@ -1670,6 +1675,13 @@
}
}
+ private List<ShortcutInfo> setReturnedByServer(List<ShortcutInfo> shortcuts) {
+ for (int i = shortcuts.size() - 1; i >= 0; i--) {
+ shortcuts.get(i).setReturnedByServer();
+ }
+ return shortcuts;
+ }
+
// === APIs ===
@Override
@@ -2049,7 +2061,7 @@
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
ps.findAll(ret, query, cloneFlags);
- return new ParceledListSlice<>(ret);
+ return new ParceledListSlice<>(setReturnedByServer(ret));
}
@Override
@@ -2406,7 +2418,7 @@
});
}
}
- return ret;
+ return setReturnedByServer(ret);
}
private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f0ae1a7..6aff600 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5485,7 +5485,8 @@
// Maintain fullscreen layout until incoming animation is complete.
topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw();
// Transient status bar on the lockscreen is not allowed
- if (mForceStatusBarFromKeyguard && mStatusBarController.isTransientShowing()) {
+ if ((mForceStatusBarFromKeyguard || statusBarExpanded)
+ && mStatusBarController.isTransientShowing()) {
mStatusBarController.updateVisibilityLw(false /*transientAllowed*/,
mLastSystemUiFlags, mLastSystemUiFlags);
}
diff --git a/services/core/java/com/android/server/storage/DiskStatsLoggingService.java b/services/core/java/com/android/server/storage/DiskStatsLoggingService.java
index 4035ade..9a08ac3 100644
--- a/services/core/java/com/android/server/storage/DiskStatsLoggingService.java
+++ b/services/core/java/com/android/server/storage/DiskStatsLoggingService.java
@@ -73,13 +73,13 @@
final int userId = UserHandle.myUserId();
UserEnvironment environment = new UserEnvironment(userId);
LogRunnable task = new LogRunnable();
- task.setRootDirectory(environment.getExternalStorageDirectory());
task.setDownloadsDirectory(
environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));
task.setSystemSize(FileCollector.getSystemSize(this));
task.setLogOutputFile(new File(DUMPSYS_CACHE_PATH));
task.setAppCollector(collector);
task.setJobService(this, params);
+ task.setContext(this);
AsyncTask.execute(task);
return true;
}
@@ -106,7 +106,8 @@
}
private static boolean isCharging(Context context) {
- BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
+ BatteryManager batteryManager =
+ (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
if (batteryManager != null) {
return batteryManager.isCharging();
}
@@ -127,14 +128,10 @@
private JobParameters mParams;
private AppCollector mCollector;
private File mOutputFile;
- private File mRootDirectory;
private File mDownloadsDirectory;
+ private Context mContext;
private long mSystemSize;
- public void setRootDirectory(File file) {
- mRootDirectory = file;
- }
-
public void setDownloadsDirectory(File file) {
mDownloadsDirectory = file;
}
@@ -151,14 +148,25 @@
mSystemSize = size;
}
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
public void setJobService(JobService jobService, JobParameters params) {
mJobService = jobService;
mParams = params;
}
public void run() {
- FileCollector.MeasurementResult mainCategories =
- FileCollector.getMeasurementResult(mRootDirectory);
+ FileCollector.MeasurementResult mainCategories;
+ try {
+ mainCategories = FileCollector.getMeasurementResult(mContext);
+ } catch (IllegalStateException e) {
+ // This can occur if installd has an issue.
+ Log.e(TAG, "Error while measuring storage", e);
+ finishJob(true);
+ return;
+ }
FileCollector.MeasurementResult downloads =
FileCollector.getMeasurementResult(mDownloadsDirectory);
@@ -168,12 +176,10 @@
needsReschedule = false;
logToFile(mainCategories, downloads, stats, mSystemSize);
} else {
- Log.w("TAG", "Timed out while fetching package stats.");
+ Log.w(TAG, "Timed out while fetching package stats.");
}
- if (mJobService != null) {
- mJobService.jobFinished(mParams, needsReschedule);
- }
+ finishJob(needsReschedule);
}
private void logToFile(MeasurementResult mainCategories, MeasurementResult downloads,
@@ -187,5 +193,11 @@
Log.e(TAG, "Exception while writing opportunistic disk file cache.", e);
}
}
+
+ private void finishJob(boolean needsReschedule) {
+ if (mJobService != null) {
+ mJobService.jobFinished(mParams, needsReschedule);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/storage/FileCollector.java b/services/core/java/com/android/server/storage/FileCollector.java
index 90f9f139..0c119a7 100644
--- a/services/core/java/com/android/server/storage/FileCollector.java
+++ b/services/core/java/com/android/server/storage/FileCollector.java
@@ -17,13 +17,17 @@
package com.android.server.storage;
import android.annotation.IntDef;
+import android.app.usage.ExternalStorageStats;
+import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.util.ArrayMap;
import java.io.File;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Map;
@@ -154,15 +158,46 @@
}
/**
+ * Returns the file categorization result for the primary internal storage UUID.
+ *
+ * @param context
+ */
+ public static MeasurementResult getMeasurementResult(Context context) {
+ MeasurementResult result = new MeasurementResult();
+ StorageStatsManager ssm =
+ (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE);
+ ExternalStorageStats stats = null;
+ try {
+ stats =
+ ssm.queryExternalStatsForUser(
+ StorageManager.UUID_PRIVATE_INTERNAL,
+ UserHandle.of(context.getUserId()));
+ result.imagesSize = stats.getImageBytes();
+ result.videosSize = stats.getVideoBytes();
+ result.audioSize = stats.getAudioBytes();
+ result.miscSize =
+ stats.getTotalBytes()
+ - result.imagesSize
+ - result.videosSize
+ - result.audioSize;
+ } catch (IOException e) {
+ throw new IllegalStateException("Could not query storage");
+ }
+
+ return result;
+ }
+
+ /**
* Returns the size of a system for a given context. This is done by finding the difference
* between the shared data and the total primary storage size.
+ *
* @param context Context to use to get storage information.
*/
public static long getSystemSize(Context context) {
PackageManager pm = context.getPackageManager();
VolumeInfo primaryVolume = pm.getPrimaryStorageCurrentVolume();
- StorageManager sm = context.getSystemService(StorageManager.class);
+ StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
VolumeInfo shared = sm.findEmulatedForPrivate(primaryVolume);
if (shared == null) {
return 0;
diff --git a/services/core/java/com/android/server/text/TextClassificationService.java b/services/core/java/com/android/server/text/TextClassificationService.java
deleted file mode 100644
index 9358238..0000000
--- a/services/core/java/com/android/server/text/TextClassificationService.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.text;
-
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.text.ITextClassificationService;
-import android.util.Slog;
-
-import com.android.server.SystemService;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-/**
- * Text classification service.
- * This is used to provide access to the text classification LSTM model file.
- */
-public class TextClassificationService extends ITextClassificationService.Stub {
-
- private static final String LOG_TAG = "TextClassificationService";
-
- public static final class Lifecycle extends SystemService {
-
- private TextClassificationService mService;
-
- public Lifecycle(Context context) {
- super(context);
- mService = new TextClassificationService();
- }
-
- @Override
- public void onStart() {
- try {
- publishBinderService(Context.TEXT_CLASSIFICATION_SERVICE, mService);
- } catch (Throwable t) {
- // Starting this service is not critical to the running of this device and should
- // therefore not crash the device. If it fails, log the error and continue.
- Slog.e(LOG_TAG, "Could not start the TextClassificationService.", t);
- }
- }
- }
-
- @Override
- public synchronized ParcelFileDescriptor getModelFileFd() throws RemoteException {
- try {
- return ParcelFileDescriptor.open(
- new File("/etc/assistant/smart-selection.model"),
- ParcelFileDescriptor.MODE_READ_ONLY);
- } catch (Throwable t) {
- Slog.e(LOG_TAG, "Error retrieving an fd to the text classification model file.", t);
- throw new RemoteException(t.getMessage());
- }
- }
-}
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 0e183f0..9ef7410 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -39,7 +39,6 @@
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
@@ -47,7 +46,6 @@
import android.service.vr.IVrListener;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
-import android.service.vr.IVrWindowManager;
import android.service.vr.VrListenerService;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -440,18 +438,6 @@
}
@Override
- public void connectController(FileDescriptor fd) throws android.os.RemoteException {
- enforceCallerPermission(Manifest.permission.RESTRICTED_VR_ACCESS);
- VrManagerService.this.connectController(fd);
- }
-
- @Override
- public void disconnectController() throws android.os.RemoteException {
- enforceCallerPermission(Manifest.permission.RESTRICTED_VR_ACCESS);
- VrManagerService.this.disconnectController();
- }
-
- @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -1184,20 +1170,4 @@
return mVrModeEnabled;
}
}
-
- private void connectController(FileDescriptor fd) throws android.os.RemoteException {
- // TODO(b/36506799): move vr_wm code to VrCore and remove this.
- IVrWindowManager remote =
- IVrWindowManager.Stub.asInterface(
- ServiceManager.getService(IVrWindowManager.SERVICE_NAME));
- remote.connectController(fd);
- }
-
- private void disconnectController() throws android.os.RemoteException {
- // TODO(b/36506799): move vr_wm code to VrCore and remove this.
- IVrWindowManager remote =
- IVrWindowManager.Stub.asInterface(
- ServiceManager.getService(IVrWindowManager.SERVICE_NAME));
- remote.disconnectController();
- }
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 7a36da2..a38addb 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -54,6 +54,7 @@
import android.graphics.Bitmap;
import android.graphics.Path;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.Debug;
import android.os.IBinder;
import android.os.IRemoteCallback;
@@ -1808,27 +1809,25 @@
final IAppTransitionAnimationSpecsFuture future
= mNextAppTransitionAnimationsSpecsFuture;
mNextAppTransitionAnimationsSpecsFuture = null;
- mDefaultExecutor.execute(new Runnable() {
- @Override
- public void run() {
- AppTransitionAnimationSpec[] specs = null;
- try {
- specs = future.get();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to fetch app transition specs: " + e);
- }
- synchronized (mService.mWindowMap) {
- mNextAppTransitionAnimationsSpecsPending = false;
- overridePendingAppTransitionMultiThumb(specs,
- mNextAppTransitionFutureCallback, null /* finishedCallback */,
- mNextAppTransitionScaleUp);
- mNextAppTransitionFutureCallback = null;
- if (specs != null) {
- mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp);
- }
- }
- mService.requestTraversal();
+ mDefaultExecutor.execute(() -> {
+ AppTransitionAnimationSpec[] specs = null;
+ try {
+ Binder.allowBlocking(future.asBinder());
+ specs = future.get();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to fetch app transition specs: " + e);
}
+ synchronized (mService.mWindowMap) {
+ mNextAppTransitionAnimationsSpecsPending = false;
+ overridePendingAppTransitionMultiThumb(specs,
+ mNextAppTransitionFutureCallback, null /* finishedCallback */,
+ mNextAppTransitionScaleUp);
+ mNextAppTransitionFutureCallback = null;
+ if (specs != null) {
+ mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp);
+ }
+ }
+ mService.requestTraversal();
});
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 92d26cb..257f285 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -759,16 +759,25 @@
return token.asAppWindowToken();
}
- void addWindowToken(IBinder binder, WindowToken token) {
+ private void addWindowToken(IBinder binder, WindowToken token) {
final DisplayContent dc = mService.mRoot.getWindowTokenDisplay(token);
if (dc != null) {
// We currently don't support adding a window token to the display if the display
// already has the binder mapped to another token. If there is a use case for supporting
// this moving forward we will either need to merge the WindowTokens some how or have
// the binder map to a list of window tokens.
- throw new IllegalArgumentException("Can't map token=" + token + " to display=" + this
- + " already mapped to display=" + dc + " tokens=" + dc.mTokenMap);
+ throw new IllegalArgumentException("Can't map token=" + token + " to display="
+ + getName() + " already mapped to display=" + dc + " tokens=" + dc.mTokenMap);
}
+ if (binder == null) {
+ throw new IllegalArgumentException("Can't map token=" + token + " to display="
+ + getName() + " binder is null");
+ }
+ if (token == null) {
+ throw new IllegalArgumentException("Can't map null token to display="
+ + getName() + " binder=" + binder);
+ }
+
mTokenMap.put(binder, token);
if (token.asAppWindowToken() == null) {
@@ -1503,16 +1512,8 @@
return mImeWindowsContainers.forAllWindows(callback, traverseTopToBottom);
}
- /**
- * Returns the orientation that this display should be in factoring in its children containers.
- *
- * @param includeAppContainers True if then app containers (stacks, tasks, ...) should be
- * factored in when determining the orientation. If false only
- * non-app/system containers will be used to determine the returned
- * orientation.
- * @return The orientation the display should be in.
- */
- int getOrientation(boolean includeAppContainers) {
+ @Override
+ int getOrientation() {
final WindowManagerPolicy policy = mService.mPolicy;
if (mService.mDisplayFrozen) {
@@ -1541,14 +1542,8 @@
}
}
- // Top system windows are not requesting an orientation. Get orientation from app containers
- // if allowed. Otherwise, return the last orientation.
- return includeAppContainers ? mTaskStackContainers.getOrientation() : mLastOrientation;
- }
-
- @Override
- int getOrientation() {
- return getOrientation(true /* includeAppContainers */);
+ // Top system windows are not requesting an orientation. Start searching from apps.
+ return mTaskStackContainers.getOrientation();
}
void updateDisplayInfo() {
@@ -2499,7 +2494,8 @@
void startKeyguardExitOnNonAppWindows(boolean onWallpaper, boolean goingToShade) {
final WindowManagerPolicy policy = mService.mPolicy;
forAllWindows(w -> {
- if (w.mAppToken == null && policy.canBeHiddenByKeyguardLw(w)) {
+ if (w.mAppToken == null && policy.canBeHiddenByKeyguardLw(w)
+ && w.wouldBeVisibleIfPolicyIgnored() && !w.isVisible()) {
w.mWinAnimator.setAnimation(
policy.createHiddenByKeyguardExit(onWallpaper, goingToShade));
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a48397b..63cc9bf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1239,7 +1239,8 @@
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
- token = new WindowToken(this, attrs.token, type, false, displayContent,
+ final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
+ token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow);
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
atoken = token.asAppWindowToken();
@@ -1310,7 +1311,7 @@
// It is not valid to use an app token with other system types; we will
// instead make a new token for it (as if null had been passed in for the token).
attrs.token = null;
- token = new WindowToken(this, null, type, false, displayContent,
+ token = new WindowToken(this, client.asBinder(), type, false, displayContent,
session.mCanAddInternalSystemWindow);
}
@@ -2373,7 +2374,7 @@
try {
synchronized(mWindowMap) {
config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded,
- displayId, true /* includeAppContainers */);
+ displayId);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2383,13 +2384,13 @@
}
private Configuration updateOrientationFromAppTokensLocked(Configuration currentConfig,
- IBinder freezeThisOneIfNeeded, int displayId, boolean includeAppContainers) {
+ IBinder freezeThisOneIfNeeded, int displayId) {
if (!mDisplayReady) {
return null;
}
Configuration config = null;
- if (updateOrientationFromAppTokensLocked(false, displayId, includeAppContainers)) {
+ if (updateOrientationFromAppTokensLocked(false, displayId)) {
// If we changed the orientation but mOrientationChangeComplete is already true,
// we used seamless rotation, and we don't need to freeze the screen.
if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) {
@@ -2427,11 +2428,6 @@
return config;
}
- boolean updateOrientationFromAppTokensLocked(boolean inTransaction, int displayId) {
- return updateOrientationFromAppTokensLocked(inTransaction, displayId,
- false /* includeAppContainers */);
- }
-
/**
* Determine the new desired orientation of the display, returning a non-null new Configuration
* if it has changed from the current orientation. IF TRUE IS RETURNED SOMEONE MUST CALL
@@ -2442,25 +2438,13 @@
* The orientation is computed from non-application windows first. If none of the
* non-application windows specify orientation, the orientation is computed from application
* tokens.
- *
- * @param inTransaction True if we are currently in a surface transaction.
- * @param displayId Id of the display to update orientation for.
- * @param includeAppContainers True if then app containers (stacks, tasks, ...) should be
- * factored in when determining the orientation. If false only
- * non-app/system containers will be used to determine the returned
- * orientation.
- * NOTE: Only call originating from activity manager are expected to
- * set this to true as it needs to synchronize several app states
- * like visibility with the update of display orientation.
- * @return True if the display orientation was updated.
* @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int)
*/
- private boolean updateOrientationFromAppTokensLocked(boolean inTransaction, int displayId,
- boolean includeAppContainers) {
- final long ident = Binder.clearCallingIdentity();
+ boolean updateOrientationFromAppTokensLocked(boolean inTransaction, int displayId) {
+ long ident = Binder.clearCallingIdentity();
try {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
- final int req = dc.getOrientation(includeAppContainers);
+ final int req = dc.getOrientation();
if (req != dc.getLastOrientation()) {
dc.setLastOrientation(req);
//send a message to Policy indicating orientation change to take
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 67516c1..25b6561 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1292,36 +1292,14 @@
@Override
boolean isVisible() {
- // TODO: The check for hiddenRequested is commented out below, because the window can still
- // be visible on screen when the flag is true. We would like the isVisible() method to
- // return an answer closer to if the window is truly visible (can't be an exact answer
- // without checking the surface state), so comment out the check for now so we can test to
- // see what problem it causes.
- // If it doesn't cause any issues, then we can remove just before we lock down the current
- // release (O) and also consolidate this method with #isVisibleUnchecked() and possibly
- // other methods like isVisibleNow().
- // If it does cause problems, then we can look if there are other ways to solve the problem.
- // If there isn't then uncomment and document here why it is needed.
- if (/*(mAppToken == null || !mAppToken.hiddenRequested) && */isVisibleUnchecked()
- // TODO: The window isn't considered visible when the token is hidden, however
- // uncommenting the check below breaks the visual transition from an app to the launcher
- // if the home buttons is pressed. Need to investigate an fix that issue before
- // uncommenting.
- /* && !mToken.hidden*/) {
- // Is this window visible? It is not visible if there is no surface, or we are in the
- // process of running an exit animation that will remove the surface, or its app token
- // has been hidden.
- return true;
- }
- return false;
+ return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility;
}
/**
- * Does the minimal check for visibility. Callers generally want to use one of the public
- * methods as they perform additional checks on the app token.
- * TODO: See if there are other places we can use this check below instead of duplicating...
+ * @return True if the window would be visible if we'd ignore policy visibility, false
+ * otherwise.
*/
- private boolean isVisibleUnchecked() {
+ boolean wouldBeVisibleIfPolicyIgnored() {
return mHasSurface && mPolicyVisibility && !isParentWindowHidden()
&& !mAnimatingExit && !mDestroying && (!mIsWallpaper || mWallpaperVisible);
}
@@ -1338,7 +1316,7 @@
// TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this?
boolean isWinVisibleLw() {
return (mAppToken == null || !mAppToken.hiddenRequested || mAppToken.mAppAnimator.animating)
- && isVisibleUnchecked();
+ && isVisible();
}
/**
@@ -1347,7 +1325,7 @@
*/
boolean isVisibleNow() {
return (!mToken.hidden || mAttrs.type == TYPE_APPLICATION_STARTING)
- && isVisibleUnchecked();
+ && isVisible();
}
/**
@@ -2063,7 +2041,7 @@
// If app died visible, apply a dim over the window to indicate that it's inactive
dc.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator);
} else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
- && dc != null && !mAnimatingExit && isVisibleUnchecked()) {
+ && dc != null && !mAnimatingExit && isVisible()) {
dc.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator);
}
}
diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp
index 813dcf5..514e996 100644
--- a/services/core/jni/com_android_server_lights_LightsService.cpp
+++ b/services/core/jni/com_android_server_lights_LightsService.cpp
@@ -29,16 +29,46 @@
namespace android {
-using ILight = ::android::hardware::light::V2_0::ILight;
using Brightness = ::android::hardware::light::V2_0::Brightness;
using Flash = ::android::hardware::light::V2_0::Flash;
-using Type = ::android::hardware::light::V2_0::Type;
+using ILight = ::android::hardware::light::V2_0::ILight;
using LightState = ::android::hardware::light::V2_0::LightState;
using Status = ::android::hardware::light::V2_0::Status;
+using Type = ::android::hardware::light::V2_0::Type;
template<typename T>
using Return = ::android::hardware::Return<T>;
-static sp<ILight> gLight;
+class LightHal {
+private:
+ static sp<ILight> sLight;
+ static bool sLightInit;
+
+ LightHal() {}
+
+public:
+ static void disassociate() {
+ sLightInit = false;
+ sLight = nullptr;
+ }
+
+ static sp<ILight> associate() {
+ if ((sLight == nullptr && !sLightInit) ||
+ (sLight != nullptr && !sLight->ping().isOk())) {
+ // will return the hal if it exists the first time.
+ sLight = ILight::getService();
+ sLightInit = true;
+
+ if (sLight == nullptr) {
+ ALOGE("Unable to get ILight interface.");
+ }
+ }
+
+ return sLight;
+ }
+};
+
+sp<ILight> LightHal::sLight = nullptr;
+bool LightHal::sLightInit = false;
static bool validate(jint light, jint flash, jint brightness) {
bool valid = true;
@@ -103,7 +133,7 @@
const LightState &state) {
if (!ret.isOk()) {
ALOGE("Failed to issue set light command.");
- gLight = nullptr;
+ LightHal::disassociate();
return;
}
@@ -137,12 +167,9 @@
return;
}
- if (gLight == nullptr || !gLight->ping().isOk()) {
- gLight = ILight::getService();
- }
+ sp<ILight> hal = LightHal::associate();
- if (gLight == nullptr) {
- ALOGE("Unable to get ILight interface.");
+ if (hal == nullptr) {
return;
}
@@ -152,7 +179,7 @@
{
ALOGD_IF_SLOW(50, "Excessive delay setting light");
- Return<Status> ret = gLight->setLight(type, state);
+ Return<Status> ret = hal->setLight(type, state);
processReturn(ret, type, state);
}
}
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 1bdcd7a..c722629 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -57,6 +57,7 @@
static jobject gPowerManagerServiceObj;
sp<IPower> gPowerHal = nullptr;
+bool gPowerHalExists = true;
std::mutex gPowerHalMutex;
static nsecs_t gLastEventTime[USER_ACTIVITY_EVENT_LAST + 1];
@@ -78,12 +79,13 @@
// Check validity of current handle to the power HAL service, and call getService() if necessary.
// The caller must be holding gPowerHalMutex.
bool getPowerHal() {
- if (gPowerHal == nullptr) {
+ if (gPowerHalExists && gPowerHal == nullptr) {
gPowerHal = IPower::getService();
if (gPowerHal != nullptr) {
ALOGI("Loaded power HAL service");
} else {
ALOGI("Couldn't load power HAL service");
+ gPowerHalExists = false;
}
}
return gPowerHal != nullptr;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 978803d..0965f03 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -104,7 +104,6 @@
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
import com.android.server.telecom.TelecomLoaderService;
-import com.android.server.text.TextClassificationService;
import com.android.server.trust.TrustManagerService;
import com.android.server.tv.TvInputManagerService;
import com.android.server.tv.TvRemoteService;
@@ -1042,12 +1041,6 @@
traceEnd();
}
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartTextClassificationService");
- mSystemServiceManager.startService(TextClassificationService.Lifecycle.class);
- traceEnd();
- }
-
if (!disableNetwork) {
traceBeginAndSlog("StartNetworkScoreService");
try {
diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
index 2e8b068..0cf4994 100644
--- a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
@@ -69,7 +69,7 @@
Notification n = builder.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true);
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
return r;
}
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 5e8b3d4..d4904f5 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -259,7 +259,7 @@
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true);
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
mService.addNotification(r);
return r;
}
@@ -769,7 +769,7 @@
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true);
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
mService.addNotification(r);
mService.buzzBeepBlinkLocked(r);
diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
index 33d2d07..24cb72e 100644
--- a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
@@ -53,21 +53,21 @@
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
left.setGlobalSortKey("first");
NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
right.setGlobalSortKey("second");
NotificationRecord last = new NotificationRecord(InstrumentationRegistry.getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
final List<NotificationRecord> expected = new ArrayList<>();
@@ -93,13 +93,13 @@
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
right.setGlobalSortKey("not null");
final List<NotificationRecord> expected = new ArrayList<>();
@@ -124,14 +124,14 @@
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
left.setGlobalSortKey("not null");
NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
final List<NotificationRecord> expected = new ArrayList<>();
expected.add(left);
diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
index 7a2dbaf..3dbd803 100644
--- a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -71,7 +71,7 @@
Notification n = builder.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true);
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
return r;
}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
index ccd2db0..84945ab 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
@@ -108,7 +108,7 @@
.build();
mRecordMinCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
callPkg, 1, "minCall", callUid, callUid, n1,
- new UserHandle(userId), "", 2000), getDefaultChannel(), false);
+ new UserHandle(userId), "", 2000), getDefaultChannel());
mRecordMinCall.setUserImportance(NotificationManager.IMPORTANCE_MIN);
Notification n2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
@@ -118,7 +118,7 @@
.build();
mRecordHighCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
callPkg, 1, "highcall", callUid, callUid, n2,
- new UserHandle(userId), "", 1999), getDefaultChannel(), false);
+ new UserHandle(userId), "", 1999), getDefaultChannel());
mRecordHighCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
Notification n3 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
@@ -128,14 +128,14 @@
.build();
mRecordDefaultMedia = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "media", uid2, uid2, n3, new UserHandle(userId),
- "", 1499), getDefaultChannel(), false);
+ "", 1499), getDefaultChannel());
mRecordDefaultMedia.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
Notification n4 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setStyle(new Notification.MessagingStyle("sender!")).build();
mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId),
- "", 1599), getDefaultChannel(), false);
+ "", 1599), getDefaultChannel());
mRecordInlineReply.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
mRecordInlineReply.setPackagePriority(Notification.PRIORITY_MAX);
@@ -143,27 +143,27 @@
.setCategory(Notification.CATEGORY_MESSAGE).build();
mRecordSms = new NotificationRecord(mContext, new StatusBarNotification(smsPkg,
smsPkg, 1, "sms", smsUid, smsUid, n5, new UserHandle(userId),
- "", 1299), getDefaultChannel(), false);
+ "", 1299), getDefaultChannel());
mRecordSms.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
Notification n6 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build();
mRecordStarredContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "starred", uid2, uid2, n6, new UserHandle(userId),
- "", 1259), getDefaultChannel(), false);
+ "", 1259), getDefaultChannel());
mRecordStarredContact.setContactAffinity(ValidateNotificationPeople.STARRED_CONTACT);
mRecordStarredContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
Notification n7 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build();
mRecordContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "contact", uid2, uid2, n7, new UserHandle(userId),
- "", 1259), getDefaultChannel(), false);
+ "", 1259), getDefaultChannel());
mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT);
mRecordContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
Notification n8 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build();
mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "urgent", uid2, uid2, n8, new UserHandle(userId),
- "", 1258), getDefaultChannel(), false);
+ "", 1258), getDefaultChannel());
mRecordUrgent.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
Notification n9 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
@@ -173,7 +173,7 @@
.build();
mRecordCheater = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "cheater", uid2, uid2, n9, new UserHandle(userId),
- "", 9258), getDefaultChannel(), false);
+ "", 9258), getDefaultChannel());
mRecordCheater.setUserImportance(NotificationManager.IMPORTANCE_LOW);
mRecordCheater.setPackagePriority(Notification.PRIORITY_MAX);
@@ -181,7 +181,7 @@
.setStyle(new Notification.InboxStyle().setSummaryText("message!")).build();
mRecordEmail = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "email", uid2, uid2, n10, new UserHandle(userId),
- "", 1599), getDefaultChannel(), false);
+ "", 1599), getDefaultChannel());
mRecordEmail.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
Notification n11 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
@@ -190,7 +190,7 @@
.build();
mRecordCheaterColorized = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "cheater", uid2, uid2, n11, new UserHandle(userId),
- "", 9258), getDefaultChannel(), false);
+ "", 9258), getDefaultChannel());
mRecordCheaterColorized.setUserImportance(NotificationManager.IMPORTANCE_LOW);
}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index f3eb4f8..177c02d 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -165,7 +165,7 @@
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id, "tag", uid, 0,
nb.build(), new UserHandle(uid), null, 0);
- return new NotificationRecord(mContext, sbn, channel, true);
+ return new NotificationRecord(mContext, sbn, channel);
}
private NotificationRecord generateNotificationRecord(NotificationChannel channel) {
return generateNotificationRecord(channel, null);
@@ -184,7 +184,7 @@
}
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", uid, 0,
nb.build(), new UserHandle(uid), null, 0);
- return new NotificationRecord(mContext, sbn, channel, true);
+ return new NotificationRecord(mContext, sbn, channel);
}
@Test
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
index 66f3799..1c8ca84 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
@@ -21,6 +21,7 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
@@ -60,10 +61,6 @@
private final Context mMockContext = Mockito.mock(Context.class);
@Mock PackageManager mPm;
- // constants for targetSdk version. N is pre channels, O is post.
- private final boolean N = false;
- private final boolean O = true;
-
private final String pkg = "com.android.server.notification";
private final int uid = 9583;
private final String pkg2 = "pkg2";
@@ -79,6 +76,7 @@
new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "test",
NotificationManager.IMPORTANCE_UNSPECIFIED);
private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+ final ApplicationInfo legacy = new ApplicationInfo();
final ApplicationInfo upgrade = new ApplicationInfo();
private static final long[] CUSTOM_VIBRATION = new long[] {
@@ -102,13 +100,17 @@
InstrumentationRegistry.getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
+ legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
upgrade.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
- when(mMockContext.getApplicationInfo()).thenReturn(upgrade);
+ try {
+ when(mPm.getApplicationInfoAsUser(eq(pkg), anyInt(), anyInt())).thenReturn(legacy);
+ when(mPm.getApplicationInfoAsUser(eq(pkg2), anyInt(), anyInt())).thenReturn(upgrade);
+ } catch (PackageManager.NameNotFoundException e) {}
}
- private StatusBarNotification getNotification(boolean supportsChannels, boolean noisy,
- boolean defaultSound, boolean buzzy, boolean defaultVibration, boolean lights,
- boolean defaultLights) {
+ private StatusBarNotification getNotification(boolean preO, boolean noisy, boolean defaultSound,
+ boolean buzzy, boolean defaultVibration, boolean lights, boolean defaultLights) {
+ when(mMockContext.getApplicationInfo()).thenReturn(preO ? legacy : upgrade);
final Builder builder = new Builder(mMockContext)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -147,13 +149,13 @@
}
builder.setDefaults(defaults);
- if (supportsChannels) {
+ if (!preO) {
builder.setChannelId(channelId);
}
Notification n = builder.build();
- if (!supportsChannels) {
+ if (preO) {
return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n,
mUser, null, uid);
} else {
@@ -170,11 +172,11 @@
public void testSound_default_preUpgradeUsesNotification() throws Exception {
defaultChannel.setSound(null, null);
// pre upgrade, default sound.
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound());
assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, record.getAudioAttributes());
}
@@ -183,11 +185,11 @@
public void testSound_custom_preUpgradeUsesNotification() throws Exception {
defaultChannel.setSound(null, null);
// pre upgrade, custom sound.
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(CUSTOM_SOUND, record.getSound());
assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes());
}
@@ -197,11 +199,11 @@
defaultChannel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
defaultChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
// pre upgrade, default sound.
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(CUSTOM_SOUND, record.getSound());
assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes());
}
@@ -209,11 +211,11 @@
@Test
public void testSound_noSound_preUpgrade() throws Exception {
// pre upgrade, default sound.
- StatusBarNotification sbn = getNotification(N, false /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(null, record.getSound());
assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, record.getAudioAttributes());
}
@@ -222,11 +224,11 @@
public void testSound_default_upgradeUsesChannel() throws Exception {
channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
// post upgrade, default sound.
- StatusBarNotification sbn = getNotification(O, true /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(CUSTOM_SOUND, record.getSound());
assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes());
}
@@ -235,11 +237,11 @@
public void testVibration_default_preUpgradeUsesNotification() throws Exception {
defaultChannel.enableVibration(false);
// pre upgrade, default vibration.
- StatusBarNotification sbn = getNotification(N, false /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
false /* defaultSound */, true /* buzzy */, true /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertNotNull(record.getVibration());
}
@@ -247,11 +249,11 @@
public void testVibration_custom_preUpgradeUsesNotification() throws Exception {
defaultChannel.enableVibration(false);
// pre upgrade, custom vibration.
- StatusBarNotification sbn = getNotification(N, false /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(CUSTOM_VIBRATION, record.getVibration());
}
@@ -260,11 +262,11 @@
defaultChannel.enableVibration(true);
defaultChannel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
// pre upgrade, custom vibration.
- StatusBarNotification sbn = getNotification(N, false /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertTrue(!Objects.equals(CUSTOM_VIBRATION, record.getVibration()));
}
@@ -272,20 +274,20 @@
public void testVibration_custom_upgradeUsesChannel() throws Exception {
channel.enableVibration(true);
// post upgrade, custom vibration.
- StatusBarNotification sbn = getNotification(O, false /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */,
false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(CUSTOM_CHANNEL_VIBRATION, record.getVibration());
}
@Test
public void testImportance_preUpgrade() throws Exception {
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(NotificationManager.IMPORTANCE_HIGH, record.getImportance());
}
@@ -293,11 +295,11 @@
public void testImportance_locked_preUpgrade() throws Exception {
defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
defaultChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(NotificationManager.IMPORTANCE_LOW, record.getImportance());
}
@@ -305,39 +307,39 @@
public void testImportance_locked_unspecified_preUpgrade() throws Exception {
defaultChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
defaultChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(NotificationManager.IMPORTANCE_HIGH, record.getImportance());
}
@Test
public void testImportance_upgrade() throws Exception {
- StatusBarNotification sbn = getNotification(O, true /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(NotificationManager.IMPORTANCE_DEFAULT, record.getImportance());
}
@Test
public void testLights_preUpgrade_noLight() throws Exception {
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertNull(record.getLight());
}
@Test
public void testLights_preUpgrade() throws Exception {
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
true /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(CUSTOM_LIGHT, record.getLight());
}
@@ -345,11 +347,11 @@
public void testLights_locked_preUpgrade() throws Exception {
defaultChannel.enableLights(true);
defaultChannel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
true /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertFalse(CUSTOM_LIGHT.equals(record.getLight()));
}
@@ -364,10 +366,10 @@
NotificationRecord.Light expected = new NotificationRecord.Light(
defaultLightColor, defaultLightOn, defaultLightOff);
- StatusBarNotification sbn = getNotification(O, true /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
true /* lights */, true /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(expected, record.getLight());
}
@@ -380,19 +382,19 @@
NotificationRecord.Light expected = new NotificationRecord.Light(
Color.BLUE, defaultLightOn, defaultLightOff);
- StatusBarNotification sbn = getNotification(O, true /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
true /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(expected, record.getLight());
}
@Test
public void testLights_upgrade_noLight() throws Exception {
- StatusBarNotification sbn = getNotification(O, true /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertNull(record.getLight());
}
}
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 8ab6d08..7bef033 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -124,7 +124,7 @@
.build();
mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification(
"package", "package", 1, null, 0, 0, mNotiGroupGSortA, user,
- null, System.currentTimeMillis()), getDefaultChannel(), false);
+ null, System.currentTimeMillis()), getDefaultChannel());
mNotiGroupGSortB = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.setContentTitle("B")
@@ -134,7 +134,7 @@
.build();
mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification(
"package", "package", 1, null, 0, 0, mNotiGroupGSortB, user,
- null, System.currentTimeMillis()), getDefaultChannel(), false);
+ null, System.currentTimeMillis()), getDefaultChannel());
mNotiNoGroup = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.setContentTitle("C")
@@ -142,7 +142,7 @@
.build();
mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification(
"package", "package", 1, null, 0, 0, mNotiNoGroup, user,
- null, System.currentTimeMillis()), getDefaultChannel(), false);
+ null, System.currentTimeMillis()), getDefaultChannel());
mNotiNoGroup2 = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.setContentTitle("D")
@@ -150,7 +150,7 @@
.build();
mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification(
"package", "package", 1, null, 0, 0, mNotiNoGroup2, user,
- null, System.currentTimeMillis()), getDefaultChannel(), false);
+ null, System.currentTimeMillis()), getDefaultChannel());
mNotiNoGroupSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.setContentTitle("E")
@@ -159,7 +159,7 @@
.build();
mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
"package", "package", 1, null, 0, 0, mNotiNoGroupSortA, user,
- null, System.currentTimeMillis()), getDefaultChannel(), false);
+ null, System.currentTimeMillis()), getDefaultChannel());
mAudioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
@@ -373,7 +373,7 @@
assertNull(mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
assertNull(mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG, UID));
- assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG, UID));
+ //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG, UID));
assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
}
@@ -696,14 +696,20 @@
// Returns only non-deleted channels
List<NotificationChannel> channels =
mHelper.getNotificationChannels(PKG, UID, false).getList();
- assertEquals(1, channels.size());
- compareChannels(channel2, channels.get(0));
+ assertEquals(2, channels.size()); // Default channel + non-deleted channel
+ for (NotificationChannel nc : channels) {
+ if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+ compareChannels(channel2, nc);
+ }
+ }
// Returns deleted channels too
channels = mHelper.getNotificationChannels(PKG, UID, true).getList();
- assertEquals(2, channels.size());
+ assertEquals(3, channels.size()); // Includes default channel
for (NotificationChannel nc : channels) {
- compareChannels(channelMap.get(nc.getId()), nc);
+ if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+ compareChannels(channelMap.get(nc.getId()), nc);
+ }
}
}
@@ -801,8 +807,8 @@
mHelper.permanentlyDeleteNotificationChannels(PKG, UID);
- // No channels remain
- assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
+ // Only default channel remains
+ assertEquals(1, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
}
@Test
@@ -881,32 +887,13 @@
mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
- // since this is a pre upgrade app, clearing data should restore the default channel
- assertEquals(1, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
- assertEquals(NotificationChannel.DEFAULT_CHANNEL_ID,
- mHelper.getNotificationChannels(PKG, UID, true).getList().get(0).getId());
+ assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
// Not deleted
mHelper.createNotificationChannel(PKG, UID, channel1, true);
+
mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
- assertEquals(1, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
- }
-
- @Test
- public void testOnPackageChanged_packageRemoval_updatedPackage() throws Exception {
- // Deleted
- NotificationChannel channel1 =
- new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, channel1, true);
- mHelper.onPackagesChanged(
- true, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2});
- assertEquals(0, mHelper.getNotificationChannels(UPDATED_PKG, UID2, true).getList().size());
-
- // Not deleted
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, channel1, true);
- mHelper.onPackagesChanged(
- false, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2});
- assertEquals(1, mHelper.getNotificationChannels(UPDATED_PKG, UID2, false).getList().size());
+ assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
}
@Test
@@ -927,23 +914,7 @@
mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
- // default channel restored
- assertEquals(1, mHelper.getNotificationChannelGroups(PKG, UID, true).getList().size());
- assertNull(mHelper.getNotificationChannelGroups(PKG, UID, true).getList().get(0).getId());
- }
-
- @Test
- public void testOnPackageChanged_packageRemoval_groups_upgraded() throws Exception {
- NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(UPDATED_PKG, UID2, ncg, true);
- NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
- mHelper.createNotificationChannelGroup(UPDATED_PKG, UID2, ncg2, true);
-
- mHelper.onPackagesChanged(
- true, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2});
-
- assertEquals(0,
- mHelper.getNotificationChannelGroups(UPDATED_PKG, UID2, true).getList().size());
+ assertEquals(0, mHelper.getNotificationChannelGroups(PKG, UID, true).getList().size());
}
@Test
@@ -1017,8 +988,9 @@
assertEquals(3, actual.size());
for (NotificationChannelGroup group : actual) {
if (group.getId() == null) {
- assertEquals(1, group.getChannels().size());
- assertTrue(channel3.getId().equals(group.getChannels().get(0).getId()));
+ assertEquals(2, group.getChannels().size()); // misc channel too
+ assertTrue(channel3.getId().equals(group.getChannels().get(0).getId())
+ || channel3.getId().equals(group.getChannels().get(1).getId()));
} else if (group.getId().equals(ncg.getId())) {
assertEquals(2, group.getChannels().size());
if (group.getChannels().get(0).getId().equals(channel1.getId())) {
@@ -1052,8 +1024,12 @@
List<NotificationChannelGroup> actual =
mHelper.getNotificationChannelGroups(PKG, UID, true).getList();
- assertEquals(1, actual.size());
- assertEquals(1, actual.get(0).getChannels().size());
+ assertEquals(2, actual.size());
+ for (NotificationChannelGroup group : actual) {
+ if (Objects.equals(group.getId(), ncg.getId())) {
+ assertEquals(1, group.getChannels().size());
+ }
+ }
}
@Test
diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
index b1cb4d7..bc25860 100644
--- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
@@ -312,7 +312,7 @@
TEST_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_LOW);
return new NotificationRecord(getContext(), new StatusBarNotification(
pkg, pkg, id, tag, 0, 0, n, user, null,
- System.currentTimeMillis()), notificationChannel, true);
+ System.currentTimeMillis()), notificationChannel);
}
private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
diff --git a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
index 9a9c243..58a4456 100644
--- a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
@@ -16,10 +16,12 @@
package com.android.server;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.content.Context;
import android.content.ContextWrapper;
+import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Secure;
@@ -32,6 +34,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.display.DisplayTransformManager;
import com.android.server.display.NightDisplayService;
+import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
import org.junit.After;
@@ -41,6 +44,10 @@
import org.mockito.Mockito;
import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.doReturn;
@@ -51,7 +58,7 @@
private Context mContext;
private int mUserId;
- private TwilightManager mTwilightManager;
+ private MockTwilightManager mTwilightManager;
private NightDisplayController mNightDisplayController;
private NightDisplayService mNightDisplayService;
@@ -73,7 +80,7 @@
final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class);
LocalServices.addService(DisplayTransformManager.class, dtm);
- mTwilightManager = Mockito.mock(TwilightManager.class);
+ mTwilightManager = new MockTwilightManager();
LocalServices.addService(TwilightManager.class, mTwilightManager);
mNightDisplayController = new NightDisplayController(mContext, mUserId);
@@ -526,23 +533,371 @@
assertActivated(true /* activated */);
}
- /**
- * Convenience for making a {@link LocalTime} instance with an offset relative to now.
- *
- * @param offsetMinutes the offset relative to now (in minutes)
- * @return the LocalTime instance
- */
- private LocalTime getLocalTimeRelativeToNow(int offsetMinutes) {
- final Calendar c = Calendar.getInstance();
- c.add(Calendar.MINUTE, offsetMinutes);
- return new LocalTime(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOffAfterNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOffBeforeNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, -180 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOffDuringNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOffInFuture_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOnAfterNight_turnsOn() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOnBeforeNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, -180 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOnDuringNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOnInFuture_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOffAfterNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, 180 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOffBeforeNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOffDuringNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOffInPast_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOnAfterNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, 180 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOnBeforeNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOnDuringNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOnInPast_turnsOn() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOffAfterNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOffBeforeNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOffDuringNightInFuture_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOffDuringNightInPast_turnsOff() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOnAfterNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOnBeforeNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOnDuringNightInFuture_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOnDuringNightInPast_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
}
/**
* Configures Night display to use a custom schedule.
*
* @param startTimeOffset the offset relative to now to activate Night display (in minutes)
- * @param endTimeOffset the offset relative to now to deactivate Night display (in minutes)
+ * @param endTimeOffset the offset relative to now to deactivate Night display (in minutes)
*/
private void setAutoModeCustom(int startTimeOffset, int endTimeOffset) {
mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_CUSTOM);
@@ -553,34 +908,21 @@
/**
* Configures Night display to use the twilight schedule.
*
- * @param sunsetOffset the offset relative to now for sunset (in minutes)
+ * @param sunsetOffset the offset relative to now for sunset (in minutes)
* @param sunriseOffset the offset relative to now for sunrise (in minutes)
*/
private void setAutoModeTwilight(int sunsetOffset, int sunriseOffset) {
mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_TWILIGHT);
-
- final LocalTime sunset = getLocalTimeRelativeToNow(sunsetOffset);
- final LocalTime sunrise = getLocalTimeRelativeToNow(sunriseOffset);
-
- final Calendar now = Calendar.getInstance();
- long sunsetMillis = sunset.getDateTimeBefore(now).getTimeInMillis();
- long sunriseMillis = sunrise.getDateTimeBefore(now).getTimeInMillis();
- if (sunsetMillis < sunriseMillis) {
- sunsetMillis = sunset.getDateTimeAfter(now).getTimeInMillis();
- } else {
- sunriseMillis = sunrise.getDateTimeAfter(now).getTimeInMillis();
- }
-
- final TwilightState state = new TwilightState(sunriseMillis, sunsetMillis);
- doReturn(state).when(mTwilightManager).getLastTwilightState();
+ mTwilightManager.setTwilightState(
+ getTwilightStateRelativeToNow(sunsetOffset, sunriseOffset));
}
/**
* Configures the Night display activated state.
*
- * @param activated {@code true} if Night display should be activated
+ * @param activated {@code true} if Night display should be activated
* @param lastActivatedTimeOffset the offset relative to now to record that Night display was
- activated (in minutes)
+ * activated (in minutes)
*/
private void setActivated(boolean activated, int lastActivatedTimeOffset) {
mNightDisplayController.setActivated(activated);
@@ -617,4 +959,93 @@
.that(mNightDisplayController.isActivated())
.isEqualTo(activated);
}
+
+ /**
+ * Convenience for making a {@link LocalTime} instance with an offset relative to now.
+ *
+ * @param offsetMinutes the offset relative to now (in minutes)
+ * @return the LocalTime instance
+ */
+ private static LocalTime getLocalTimeRelativeToNow(int offsetMinutes) {
+ final Calendar c = Calendar.getInstance();
+ c.add(Calendar.MINUTE, offsetMinutes);
+ return new LocalTime(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
+ }
+
+ /**
+ * Convenience for making a {@link TwilightState} instance with sunrise/sunset relative to now.
+ *
+ * @param sunsetOffset the offset relative to now for sunset (in minutes)
+ * @param sunriseOffset the offset relative to now for sunrise (in minutes)
+ * @return the TwilightState instance
+ */
+ private static TwilightState getTwilightStateRelativeToNow(int sunsetOffset,
+ int sunriseOffset) {
+ final LocalTime sunset = getLocalTimeRelativeToNow(sunsetOffset);
+ final LocalTime sunrise = getLocalTimeRelativeToNow(sunriseOffset);
+
+ final Calendar now = Calendar.getInstance();
+ long sunsetMillis = sunset.getDateTimeBefore(now).getTimeInMillis();
+ long sunriseMillis = sunrise.getDateTimeBefore(now).getTimeInMillis();
+ if (sunsetMillis < sunriseMillis) {
+ sunsetMillis = sunset.getDateTimeAfter(now).getTimeInMillis();
+ } else {
+ sunriseMillis = sunrise.getDateTimeAfter(now).getTimeInMillis();
+ }
+
+ return new TwilightState(sunriseMillis, sunsetMillis);
+ }
+
+ private static class MockTwilightManager implements TwilightManager {
+
+ private final Map<TwilightListener, Handler> mListeners = new HashMap<>();
+ private TwilightState mTwilightState;
+
+ /**
+ * Updates the TwilightState and notifies any registered listeners.
+ *
+ * @param state the new TwilightState to use
+ */
+ void setTwilightState(TwilightState state) {
+ synchronized (mListeners) {
+ mTwilightState = state;
+
+ final CountDownLatch latch = new CountDownLatch(mListeners.size());
+ for (Map.Entry<TwilightListener, Handler> entry : mListeners.entrySet()) {
+ entry.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onTwilightStateChanged(state);
+ latch.countDown();
+ }
+ });
+ }
+
+ try {
+ latch.await(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public void registerListener(@NonNull TwilightListener listener, @NonNull Handler handler) {
+ synchronized (mListeners) {
+ mListeners.put(listener, handler);
+ }
+ }
+
+ @Override
+ public void unregisterListener(@NonNull TwilightListener listener) {
+ synchronized (mListeners) {
+ mListeners.remove(listener);
+ }
+ }
+
+ @Override
+ public TwilightState getLastTwilightState() {
+ return mTwilightState;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java
index ace65a6..02f645a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java
@@ -54,6 +54,7 @@
private static int OTHER_VIEW_ID = 0xCAB2;
private static int PARENT_VIEW_ID = 0xFED4;
private static int CHILD_VIEW_ID = 0xFEED;
+ private static int OTHER_CHILD_VIEW_ID = 0xACE2;
private static int MOCK_CONNECTION_ID = 1;
AccessibilityCache mAccessibilityCache;
@@ -482,6 +483,30 @@
}
@Test
+ public void addNode_whenNodeBeingReplacedIsOwnGrandparent_doesntCrash() {
+ AccessibilityNodeInfo parentNodeInfo =
+ getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
+ parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID, WINDOW_ID_1));
+ parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(OTHER_CHILD_VIEW_ID, WINDOW_ID_1));
+ AccessibilityNodeInfo childNodeInfo =
+ getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
+ childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
+ childNodeInfo.addChild(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
+
+ AccessibilityNodeInfo replacementParentNodeInfo =
+ getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
+ try {
+ mAccessibilityCache.add(parentNodeInfo);
+ mAccessibilityCache.add(childNodeInfo);
+ mAccessibilityCache.add(replacementParentNodeInfo);
+ } finally {
+ parentNodeInfo.recycle();
+ childNodeInfo.recycle();
+ replacementParentNodeInfo.recycle();
+ }
+ }
+
+ @Test
public void testCacheCriticalEventList_doesntLackEvents() {
for (int i = 0; i < 32; i++) {
int eventType = 1 << i;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index c87eaed..711c36b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.platform.test.annotations.Presubmit;
@@ -97,4 +98,25 @@
testStack.stopActivityLocked(activityRecord);
}
+
+ /**
+ * This test verifies that {@link ActivityStack#STACK_VISIBLE_ACTIVITY_BEHIND} is returned from
+ * {@link ActivityStack#shouldBeVisible(ActivityRecord)} from a fullscreen workspace stack with
+ * a visible behind activity when top focused stack is the home stack.
+ */
+ @Test
+ public void testShouldBeVisibleWithVisibleBehindActivity() throws Exception {
+ final ActivityManagerService service = createActivityManagerService();
+ final TaskRecord task = createTask(service, testActivityComponent,
+ ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID);
+ final ActivityStack fullscreenWorkspaceStackId = task.getStack();
+ final ActivityStack homeStack = service.mStackSupervisor.getStack(
+ ActivityManager.StackId.HOME_STACK_ID, true /*createStaticStackIfNeeded*/,
+ true /*onTop*/);
+ final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
+ service.mStackSupervisor.setFocusStackUnchecked("testEmptyStackShouldBeVisible", homeStack);
+ service.mStackSupervisor.requestVisibleBehindLocked(activityRecord, true);
+ assertEquals(ActivityStack.STACK_VISIBLE_ACTIVITY_BEHIND,
+ fullscreenWorkspaceStackId.shouldBeVisible(null /*starting*/));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 28051f9..9cfa542 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -151,9 +151,12 @@
* setup not available in the test environment. Also specifies an injector for
*/
protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
+ private final ActivityDisplay mDisplay;
+
public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
super(service, looper);
mWindowManager = prepareMockWindowManager();
+ mDisplay = new ActivityDisplay();
}
// No home stack is set.
@@ -185,9 +188,8 @@
public <T extends ActivityStack> T createTestStack(ActivityManagerService service,
int stackId, boolean onTop) {
- final ActivityDisplay display = new ActivityDisplay();
final TestActivityContainer container =
- new TestActivityContainer(service, stackId, display, onTop);
+ new TestActivityContainer(service, stackId, mDisplay, onTop);
mActivityContainers.put(stackId, container);
return (T) container.getStack();
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 4c7bf4d..f4944f9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -7399,4 +7399,50 @@
"s21", "s22");
});
}
+
+ public void testReturnedByServer() {
+ // Package 1 updated, with manifest shortcuts.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(mManager.getManifestShortcuts())
+ .haveIds("ms1")
+ .forAllShortcuts(si -> assertTrue(si.isReturnedByServer()));
+
+ assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+
+ assertWith(mManager.getDynamicShortcuts())
+ .haveIds("s1")
+ .forAllShortcuts(si -> assertTrue(si.isReturnedByServer()));
+ });
+
+ // Pin them.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("ms1", "s1"), getCallingUser());
+ assertWith(getShortcutAsLauncher(USER_0))
+ .haveIds("ms1", "s1")
+ .forAllShortcuts(si -> assertTrue(si.isReturnedByServer()));
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(mManager.getPinnedShortcuts())
+ .haveIds("ms1", "s1")
+ .forAllShortcuts(si -> assertTrue(si.isReturnedByServer()));
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ // This shows a warning log, but should still work.
+ assertTrue(mManager.setDynamicShortcuts(mManager.getDynamicShortcuts()));
+
+ assertWith(mManager.getDynamicShortcuts())
+ .haveIds("s1")
+ .forAllShortcuts(si -> assertTrue(si.isReturnedByServer()));
+ });
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
index 3789086..375edf3 100644
--- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
@@ -20,16 +20,31 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.eq;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.job.JobService;
-import android.app.job.JobParameters;
+import android.app.job.JobServiceEngine;
+import android.app.usage.ExternalStorageStats;
+import android.app.usage.StorageStatsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
+import android.os.BatteryManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.provider.Settings;
import android.test.AndroidTestCase;
+import android.test.mock.MockContentResolver;
+import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.storage.DiskStatsLoggingService.LogRunnable;
import libcore.io.IoUtils;
@@ -46,14 +61,17 @@
import java.io.File;
import java.io.PrintStream;
+import java.lang.reflect.Field;
import java.util.ArrayList;
@RunWith(JUnit4.class)
public class DiskStatsLoggingServiceTest extends AndroidTestCase {
@Rule public TemporaryFolder mTemporaryFolder;
@Rule public TemporaryFolder mDownloads;
- @Rule public TemporaryFolder mRootDirectory;
@Mock private AppCollector mCollector;
+ @Mock private JobService mJobService;
+ @Mock private StorageStatsManager mSsm;
+ private ExternalStorageStats mStorageStats;
private File mInputFile;
@@ -66,8 +84,10 @@
mInputFile = mTemporaryFolder.newFile();
mDownloads = new TemporaryFolder();
mDownloads.create();
- mRootDirectory = new TemporaryFolder();
- mRootDirectory.create();
+ mStorageStats = new ExternalStorageStats();
+ when(mSsm.queryExternalStatsForUser(isNull(String.class), any(UserHandle.class)))
+ .thenReturn(mStorageStats);
+ when(mJobService.getSystemService(anyString())).thenReturn(mSsm);
}
@Test
@@ -75,9 +95,9 @@
LogRunnable task = new LogRunnable();
task.setAppCollector(mCollector);
task.setDownloadsDirectory(mDownloads.getRoot());
- task.setRootDirectory(mRootDirectory.getRoot());
task.setLogOutputFile(mInputFile);
task.setSystemSize(0L);
+ task.setContext(mJobService);
task.run();
JSONObject json = getJsonOutput();
@@ -99,10 +119,10 @@
public void testPopulatedLogTask() throws Exception {
// Write data to directories.
writeDataToFile(mDownloads.newFile(), "lol");
- writeDataToFile(mRootDirectory.newFile("test.jpg"), "1234");
- writeDataToFile(mRootDirectory.newFile("test.mp4"), "12345");
- writeDataToFile(mRootDirectory.newFile("test.mp3"), "123456");
- writeDataToFile(mRootDirectory.newFile("test.whatever"), "1234567");
+ mStorageStats.audioBytes = 6L;
+ mStorageStats.imageBytes = 4L;
+ mStorageStats.videoBytes = 5L;
+ mStorageStats.totalBytes = 22L;
// Write apps.
ArrayList<PackageStats> apps = new ArrayList<>();
@@ -110,15 +130,16 @@
testApp.dataSize = 5L;
testApp.cacheSize = 55L;
testApp.codeSize = 10L;
+ testApp.userHandle = UserHandle.USER_SYSTEM;
apps.add(testApp);
- when(mCollector.getPackageStats(anyInt())).thenReturn(apps);
+ when(mCollector.getPackageStats(anyLong())).thenReturn(apps);
LogRunnable task = new LogRunnable();
task.setAppCollector(mCollector);
task.setDownloadsDirectory(mDownloads.getRoot());
- task.setRootDirectory(mRootDirectory.getRoot());
task.setLogOutputFile(mInputFile);
task.setSystemSize(10L);
+ task.setContext(mJobService);
task.run();
JSONObject json = getJsonOutput();
@@ -143,14 +164,57 @@
LogRunnable task = new LogRunnable();
task.setAppCollector(mCollector);
task.setDownloadsDirectory(mDownloads.getRoot());
- task.setRootDirectory(mRootDirectory.getRoot());
task.setLogOutputFile(mInputFile);
task.setSystemSize(10L);
+ task.setContext(mJobService);
task.run();
// No exception should be thrown.
}
+ @Test
+ public void testDontCrashOnRun() throws Exception {
+ DiskStatsLoggingService service = spy(new DiskStatsLoggingService());
+ BatteryManager batteryManager = mock(BatteryManager.class);
+ when(batteryManager.isCharging()).thenReturn(true);
+ doReturn(batteryManager).when(service).getSystemService(Context.BATTERY_SERVICE);
+ UserManager userManager = mock(UserManager.class);
+ when(userManager.getUsers()).thenReturn(new ArrayList<>());
+ doReturn(userManager).when(service).getSystemService(Context.USER_SERVICE);
+ doReturn(mSsm).when(service).getSystemService(Context.STORAGE_STATS_SERVICE);
+ doReturn(mock(StorageManager.class))
+ .when(service)
+ .getSystemService(Context.STORAGE_SERVICE);
+
+ MockContentResolver cr = new MockContentResolver();
+ cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ doReturn(cr).when(service).getContentResolver();
+
+ PackageManager pm = mock(PackageManager.class);
+ VolumeInfo volumeInfo =
+ new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL, VolumeInfo.TYPE_PRIVATE, null, null);
+ when(pm.getPrimaryStorageCurrentVolume()).thenReturn(volumeInfo);
+ doReturn(pm).when(service).getPackageManager();
+
+ doReturn(0).when(service).getUserId();
+
+ // UGGGGGHHHHHHH! jobFinished is a final method on JobService which crashes when called if
+ // the JobService isn't initialized for real. ServiceTestCase doesn't let us initialize a
+ // service which is built into the framework without crashing, though, so we can't make a
+ // real initialized service.
+ //
+ // And so, we use reflection to set the JobServiceEngine, which is used by the final method,
+ // to be something which won't crash when called.
+ final Field field = JobService.class.getDeclaredField("mEngine");
+ field.setAccessible(true);
+ field.set(service, mock(JobServiceEngine.class));
+
+ // Note: This won't clobber your on-device cache file because, technically,
+ // FrameworkServicesTests don't have write permission to actually overwrite the cache file.
+ // We log and fail on the write silently in this case.
+ service.onStartJob(null);
+ }
+
private void writeDataToFile(File f, String data) throws Exception{
PrintStream out = new PrintStream(f);
out.print(data);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 47ced99..d9349ed 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -66,8 +66,7 @@
/** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
int userId) {
- final Task newTask = new Task(WindowTestUtils.sNextTaskId++, stack, userId, service, null,
- EMPTY, 0, false,
+ final Task newTask = new Task(sNextTaskId++, stack, userId, service, null, EMPTY, 0, false,
false, new ActivityManager.TaskDescription(), null);
stack.addTask(newTask, POSITION_TOP);
return newTask;
@@ -92,7 +91,7 @@
public static class TestAppWindowToken extends AppWindowToken {
TestAppWindowToken(DisplayContent dc) {
- super(dc.mService, null, false, dc, true /* fillsParent */,
+ super(dc.mService, new IApplicationToken.Stub() {}, false, dc, true /* fillsParent */,
null /* overrideConfig */, null /* bounds */);
}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index e13665b..dae74db 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -16,6 +16,8 @@
package com.android.server.usage;
+import static com.android.internal.util.ArrayUtils.defeatNullable;
+
import android.app.AppOpsManager;
import android.app.usage.ExternalStorageStats;
import android.app.usage.IStorageStatsManager;
@@ -112,9 +114,12 @@
mStorage.registerListener(new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
- if ((vol.type == VolumeInfo.TYPE_PRIVATE)
- && (newState == VolumeInfo.STATE_MOUNTED)) {
- invalidateMounts();
+ switch (vol.type) {
+ case VolumeInfo.TYPE_PRIVATE:
+ case VolumeInfo.TYPE_EMULATED:
+ if (newState == VolumeInfo.STATE_MOUNTED) {
+ invalidateMounts();
+ }
}
}
});
@@ -178,9 +183,11 @@
long cacheBytes = 0;
final long token = Binder.clearCallingIdentity();
try {
- for (UserInfo user : mUser.getUsers()) {
- final StorageStats stats = queryStatsForUser(volumeUuid, user.id, null);
- cacheBytes += stats.cacheBytes;
+ if (isQuotaSupported(volumeUuid, callingPackage)) {
+ for (UserInfo user : mUser.getUsers()) {
+ final StorageStats stats = queryStatsForUser(volumeUuid, user.id, null);
+ cacheBytes += stats.cacheBytes;
+ }
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -232,7 +239,7 @@
enforcePermission(Binder.getCallingUid(), callingPackage);
}
- if (mPackage.getPackagesForUid(appInfo.uid).length == 1) {
+ if (defeatNullable(mPackage.getPackagesForUid(appInfo.uid)).length == 1) {
// Only one package inside UID means we can fast-path
return queryStatsForUid(volumeUuid, appInfo.uid, callingPackage);
} else {
@@ -276,7 +283,7 @@
enforcePermission(Binder.getCallingUid(), callingPackage);
}
- final String[] packageNames = mPackage.getPackagesForUid(uid);
+ final String[] packageNames = defeatNullable(mPackage.getPackagesForUid(uid));
final long[] ceDataInodes = new long[packageNames.length];
String[] codePaths = new String[0];
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/tests/net/java/android/net/NetworkStatsHistoryTest.java
similarity index 99%
rename from core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
rename to tests/net/java/android/net/NetworkStatsHistoryTest.java
index 9a08f41..e7b91b5 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/tests/net/java/android/net/NetworkStatsHistoryTest.java
@@ -38,7 +38,7 @@
import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
-import com.android.frameworks.coretests.R;
+import com.android.frameworks.tests.net.R;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/NetworkStatsTest.java
rename to tests/net/java/android/net/NetworkStatsTest.java
diff --git a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
similarity index 99%
rename from core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
rename to tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
index 7f13abc..978e5f5 100644
--- a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -30,7 +30,7 @@
import android.net.TrafficStats;
import android.test.AndroidTestCase;
-import com.android.frameworks.coretests.R;
+import com.android.frameworks.tests.net.R;
import java.io.File;
import java.io.FileOutputStream;
diff --git a/core/tests/coretests/res/raw/history_v1 b/tests/net/res/raw/history_v1
similarity index 100%
rename from core/tests/coretests/res/raw/history_v1
rename to tests/net/res/raw/history_v1
Binary files differ
diff --git a/core/tests/coretests/res/raw/xt_qtaguid_iface_fmt_typical b/tests/net/res/raw/xt_qtaguid_iface_fmt_typical
similarity index 100%
rename from core/tests/coretests/res/raw/xt_qtaguid_iface_fmt_typical
rename to tests/net/res/raw/xt_qtaguid_iface_fmt_typical
diff --git a/core/tests/coretests/res/raw/xt_qtaguid_iface_typical b/tests/net/res/raw/xt_qtaguid_iface_typical
similarity index 100%
rename from core/tests/coretests/res/raw/xt_qtaguid_iface_typical
rename to tests/net/res/raw/xt_qtaguid_iface_typical
diff --git a/core/tests/coretests/res/raw/xt_qtaguid_typical b/tests/net/res/raw/xt_qtaguid_typical
similarity index 100%
rename from core/tests/coretests/res/raw/xt_qtaguid_typical
rename to tests/net/res/raw/xt_qtaguid_typical
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 03ef319..adf897d 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -22,6 +22,7 @@
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL;
+import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED;
import static org.junit.Assert.assertEquals;
@@ -335,14 +336,13 @@
*/
@Test
public void testCorrectLooperIsUsedForHandler() throws Exception {
- // record thread from looper.getThread and check ids.
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
.thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
- assertEquals(mLooper.getLooper().getThread().getId(), callback.mCallingThreadId);
+ verify(mContext, never()).getMainLooper();
}
/**
@@ -361,6 +361,7 @@
altLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
assertEquals(altLooper.getLooper().getThread().getId(), callback.mCallingThreadId);
+ verify(mContext).getMainLooper();
}
/**
@@ -464,11 +465,10 @@
}
/**
- * Verify the handler passed in to startLocalOnlyHotspot is correctly used for callbacks when a
- * null WifiConfig is returned.
+ * Verify callback triggered from startLocalOnlyHotspot with an incompatible mode failure.
*/
@Test
- public void testLocalOnlyHotspotCallbackFullOnNullConfig() throws Exception {
+ public void testLocalOnlyHotspotCallbackFullOnIncompatibleMode() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
.thenReturn(ERROR_INCOMPATIBLE_MODE);
@@ -481,6 +481,22 @@
}
/**
+ * Verify callback triggered from startLocalOnlyHotspot with a tethering disallowed failure.
+ */
+ @Test
+ public void testLocalOnlyHotspotCallbackFullOnTetheringDisallowed() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
+ .thenReturn(ERROR_TETHERING_DISALLOWED);
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+ mLooper.dispatchAll();
+ assertEquals(ERROR_TETHERING_DISALLOWED, callback.mFailureReason);
+ assertFalse(callback.mOnStartedCalled);
+ assertFalse(callback.mOnStoppedCalled);
+ assertEquals(null, callback.mRes);
+ }
+
+ /**
* Verify a SecurityException resulting from an application without necessary permissions will
* bubble up through the call to start LocalOnlyHotspot and will not trigger other callbacks.
*/
@@ -616,12 +632,11 @@
*/
@Test
public void testCorrectLooperIsUsedForObserverHandler() throws Exception {
- // record thread from looper.getThread and check ids.
TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
mWifiManager.watchLocalOnlyHotspot(observer, mHandler);
mLooper.dispatchAll();
assertTrue(observer.mOnRegistered);
- assertEquals(mLooper.getLooper().getThread().getId(), observer.mCallingThreadId);
+ verify(mContext, never()).getMainLooper();
}
/**
@@ -638,6 +653,7 @@
altLooper.dispatchAll();
assertTrue(observer.mOnRegistered);
assertEquals(altLooper.getLooper().getThread().getId(), observer.mCallingThreadId);
+ verify(mContext).getMainLooper();
}
/**