Merge "Batterystats BLE results counter takes in count" into oc-dev
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index ca4b4d5..3fd0f50 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
/**
@@ -1341,15 +1342,16 @@
// One problem is that the old node dependencies point to nodes in the old AnimatorSet.
// We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
+ HashMap<Node, Node> clonesMap = new HashMap<>(nodeCount);
for (int n = 0; n < nodeCount; n++) {
final Node node = mNodes.get(n);
Node nodeClone = node.clone();
- node.mTmpClone = nodeClone;
+ clonesMap.put(node, nodeClone);
anim.mNodes.add(nodeClone);
anim.mNodeMap.put(nodeClone.mAnimation, nodeClone);
}
- anim.mRootNode = mRootNode.mTmpClone;
+ anim.mRootNode = clonesMap.get(mRootNode);
anim.mDelayAnim = (ValueAnimator) anim.mRootNode.mAnimation;
// Now that we've cloned all of the nodes, we're ready to walk through their
@@ -1357,25 +1359,22 @@
for (int i = 0; i < nodeCount; i++) {
Node node = mNodes.get(i);
// Update dependencies for node's clone
- node.mTmpClone.mLatestParent = node.mLatestParent == null ?
- null : node.mLatestParent.mTmpClone;
+ Node nodeClone = clonesMap.get(node);
+ nodeClone.mLatestParent = node.mLatestParent == null
+ ? null : clonesMap.get(node.mLatestParent);
int size = node.mChildNodes == null ? 0 : node.mChildNodes.size();
for (int j = 0; j < size; j++) {
- node.mTmpClone.mChildNodes.set(j, node.mChildNodes.get(j).mTmpClone);
+ nodeClone.mChildNodes.set(j, clonesMap.get(node.mChildNodes.get(j)));
}
size = node.mSiblings == null ? 0 : node.mSiblings.size();
for (int j = 0; j < size; j++) {
- node.mTmpClone.mSiblings.set(j, node.mSiblings.get(j).mTmpClone);
+ nodeClone.mSiblings.set(j, clonesMap.get(node.mSiblings.get(j)));
}
size = node.mParents == null ? 0 : node.mParents.size();
for (int j = 0; j < size; j++) {
- node.mTmpClone.mParents.set(j, node.mParents.get(j).mTmpClone);
+ nodeClone.mParents.set(j, clonesMap.get(node.mParents.get(j)));
}
}
-
- for (int n = 0; n < nodeCount; n++) {
- mNodes.get(n).mTmpClone = null;
- }
return anim;
}
@@ -1748,11 +1747,6 @@
ArrayList<Node> mChildNodes = null;
/**
- * Temporary field to hold the clone in AnimatorSet#clone. Cleaned after clone is complete
- */
- private Node mTmpClone = null;
-
- /**
* Flag indicating whether the animation in this node is finished. This flag
* is used by AnimatorSet to check, as each animation ends, whether all child animations
* are mEnded and it's time to send out an end event for the entire AnimatorSet.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index e50c307..51c2246 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1696,15 +1696,27 @@
@Override
public int installExistingPackage(String packageName) throws NameNotFoundException {
- return installExistingPackageAsUser(packageName, mContext.getUserId());
+ return installExistingPackage(packageName, PackageManager.INSTALL_REASON_UNKNOWN);
+ }
+
+ @Override
+ public int installExistingPackage(String packageName, int installReason)
+ throws NameNotFoundException {
+ return installExistingPackageAsUser(packageName, installReason, mContext.getUserId());
}
@Override
public int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException {
+ return installExistingPackageAsUser(packageName, PackageManager.INSTALL_REASON_UNKNOWN,
+ userId);
+ }
+
+ private int installExistingPackageAsUser(String packageName, int installReason, int userId)
+ throws NameNotFoundException {
try {
int res = mPM.installExistingPackageAsUser(packageName, userId, 0 /*installFlags*/,
- PackageManager.INSTALL_REASON_UNKNOWN);
+ installReason);
if (res == INSTALL_FAILED_INVALID_URI) {
throw new NameNotFoundException("Package " + packageName + " doesn't exist");
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index cc7e0fd..1c1883b 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -71,6 +71,7 @@
int getDeletedChannelCount(String pkg, int uid);
void deleteNotificationChannelGroup(String pkg, String channelGroupId);
ParceledListSlice getNotificationChannelGroups(String pkg);
+ boolean onlyHasDefaultChannel(String pkg, int uid);
// TODO: Remove this when callers have been migrated to the equivalent
// INotificationListener method.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4294eab..ccbd5b4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2703,6 +2703,7 @@
private int mBackgroundColor = COLOR_INVALID;
private int mForegroundColor = COLOR_INVALID;
private int mBackgroundColorHint = COLOR_INVALID;
+ private boolean mRebuildStyledRemoteViews;
/**
* Constructs a new Builder with the defaults:
@@ -4251,7 +4252,7 @@
* @hide
*/
public RemoteViews createContentView(boolean increasedHeight) {
- if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
+ if (mN.contentView != null && useExistingRemoteView()) {
return mN.contentView;
} else if (mStyle != null) {
final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
@@ -4262,13 +4263,17 @@
return applyStandardTemplate(getBaseLayoutResource());
}
+ private boolean useExistingRemoteView() {
+ return mStyle == null || (!mStyle.displayCustomViewInline()
+ && !mRebuildStyledRemoteViews);
+ }
+
/**
* Construct a RemoteViews for the final big notification layout.
*/
public RemoteViews createBigContentView() {
RemoteViews result = null;
- if (mN.bigContentView != null
- && (mStyle == null || !mStyle.displayCustomViewInline())) {
+ if (mN.bigContentView != null && useExistingRemoteView()) {
return mN.bigContentView;
} else if (mStyle != null) {
result = mStyle.makeBigContentView();
@@ -4343,8 +4348,7 @@
* @hide
*/
public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
- if (mN.headsUpContentView != null
- && (mStyle == null || !mStyle.displayCustomViewInline())) {
+ if (mN.headsUpContentView != null && useExistingRemoteView()) {
return mN.headsUpContentView;
} else if (mStyle != null) {
final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
@@ -4806,7 +4810,7 @@
}
if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
- && (mStyle == null || !mStyle.displayCustomViewInline())) {
+ && (useExistingRemoteView())) {
if (mN.contentView == null) {
mN.contentView = createContentView();
mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
@@ -4978,6 +4982,19 @@
public void setBackgroundColorHint(int backgroundColor) {
mBackgroundColorHint = backgroundColor;
}
+
+
+ /**
+ * Forces all styled remoteViews to be built from scratch and not use any cached
+ * RemoteViews.
+ * This is needed for legacy apps that are baking in their remoteviews into the
+ * notification.
+ *
+ * @hide
+ */
+ public void setRebuildStyledRemoteViews(boolean rebuild) {
+ mRebuildStyledRemoteViews = rebuild;
+ }
}
/**
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 40fe6af..fcf1931 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -95,7 +95,6 @@
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Build;
-import android.os.Debug;
import android.os.DropBoxManager;
import android.os.HardwarePropertiesManager;
import android.os.IBatteryPropertiesRegistrar;
@@ -118,8 +117,6 @@
import android.os.storage.StorageManager;
import android.print.IPrintManager;
import android.print.PrintManager;
-import android.view.autofill.AutofillManager;
-import android.view.autofill.IAutoFillManager;
import android.service.oemlock.IOemLockService;
import android.service.oemlock.OemLockManager;
import android.service.persistentdata.IPersistentDataBlockService;
@@ -136,6 +133,8 @@
import android.view.WindowManagerImpl;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.CaptioningManager;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.IAutoFillManager;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
@@ -660,7 +659,7 @@
ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE);
ICompanionDeviceManager service =
ICompanionDeviceManager.Stub.asInterface(iBinder);
- return new CompanionDeviceManager(service, ctx);
+ return new CompanionDeviceManager(service, ctx.getOuterContext());
}});
registerService(Context.CONSUMER_IR_SERVICE, ConsumerIrManager.class,
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 1994206..eae9e1e 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -19,6 +19,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.FillContext;
+import android.service.autofill.FillRequest;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -494,7 +495,7 @@
ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false);
if ((root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
if (forAutoFill) {
- final int autofillFlags = (flags & AutofillManager.FLAG_MANUAL_REQUEST) != 0
+ final int autofillFlags = (flags & FillRequest.FLAG_MANUAL_REQUEST) != 0
? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
view.onProvideAutofillStructure(builder, autofillFlags);
} else {
@@ -506,7 +507,7 @@
}
}
if (forAutoFill) {
- final int autofillFlags = (flags & AutofillManager.FLAG_MANUAL_REQUEST) != 0
+ final int autofillFlags = (flags & FillRequest.FLAG_MANUAL_REQUEST) != 0
? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
view.dispatchProvideAutofillStructure(builder, autofillFlags);
} else {
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index fac9e13..4e70e3f 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -21,11 +21,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.Application;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
import android.content.pm.PackageManager;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.service.notification.NotificationListenerService;
@@ -137,26 +140,11 @@
}
checkNotNull(request, "Request cannot be null");
checkNotNull(callback, "Callback cannot be null");
- final Handler finalHandler = Handler.mainIfNull(handler);
try {
mService.associate(
request,
- //TODO implicit pointer to outer class -> =null onDestroy
- //TODO onStop if isFinishing -> stopScan
- new IFindDeviceCallback.Stub() {
- @Override
- public void onSuccess(PendingIntent launcher) {
- finalHandler.post(() -> {
- callback.onDeviceFound(launcher.getIntentSender());
- });
- }
-
- @Override
- public void onFailure(CharSequence reason) {
- finalHandler.post(() -> callback.onFailure(reason));
- }
- },
- mContext.getPackageName());
+ new CallbackProxy(request, callback, Handler.mainIfNull(handler)),
+ getCallingPackage());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -175,7 +163,7 @@
return Collections.emptyList();
}
try {
- return mService.getAssociations(mContext.getPackageName(), mContext.getUserId());
+ return mService.getAssociations(getCallingPackage(), mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -200,7 +188,7 @@
return;
}
try {
- mService.disassociate(deviceMacAddress, mContext.getPackageName());
+ mService.disassociate(deviceMacAddress, getCallingPackage());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -263,4 +251,57 @@
}
return featurePresent;
}
+
+ private Activity getActivity() {
+ return (Activity) mContext;
+ }
+
+ private String getCallingPackage() {
+ return mContext.getPackageName();
+ }
+
+ private class CallbackProxy extends IFindDeviceCallback.Stub
+ implements Application.ActivityLifecycleCallbacks {
+
+ private Callback mCallback;
+ private Handler mHandler;
+ private AssociationRequest mRequest;
+
+ private CallbackProxy(AssociationRequest request, Callback callback, Handler handler) {
+ mCallback = callback;
+ mHandler = handler;
+ mRequest = request;
+ getActivity().getApplication().registerActivityLifecycleCallbacks(this);
+ }
+
+ @Override
+ public void onSuccess(PendingIntent launcher) {
+ mHandler.post(() -> mCallback.onDeviceFound(launcher.getIntentSender()));
+ }
+
+ @Override
+ public void onFailure(CharSequence reason) {
+ mHandler.post(() -> mCallback.onFailure(reason));
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ try {
+ mService.stopScan(mRequest, this, getCallingPackage());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ getActivity().getApplication().unregisterActivityLifecycleCallbacks(this);
+ mCallback = null;
+ mHandler = null;
+ mRequest = null;
+ }
+
+ @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
+ @Override public void onActivityStarted(Activity activity) {}
+ @Override public void onActivityResumed(Activity activity) {}
+ @Override public void onActivityPaused(Activity activity) {}
+ @Override public void onActivityStopped(Activity activity) {}
+ @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
+ }
}
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index d395208..561342e 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -30,6 +30,9 @@
void associate(in AssociationRequest request,
in IFindDeviceCallback callback,
in String callingPackage);
+ void stopScan(in AssociationRequest request,
+ in IFindDeviceCallback callback,
+ in String callingPackage);
List<String> getAssociations(String callingPackage, int userId);
void disassociate(String deviceMacAddress, String callingPackage);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d2468d9..a68c097 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -157,6 +157,7 @@
MATCH_DISABLED_COMPONENTS,
MATCH_DISABLED_UNTIL_USED_COMPONENTS,
MATCH_INSTANT,
+ MATCH_STATIC_SHARED_LIBRARIES,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
})
@@ -177,6 +178,7 @@
MATCH_SYSTEM_ONLY,
MATCH_UNINSTALLED_PACKAGES,
MATCH_INSTANT,
+ MATCH_STATIC_SHARED_LIBRARIES,
GET_DISABLED_COMPONENTS,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
@@ -475,6 +477,16 @@
public static final int MATCH_EXPLICITLY_VISIBLE_ONLY = 0x02000000;
/**
+ * Internal {@link PackageInfo} flag: include static shared libraries.
+ * Apps that depend on static shared libs can always access the version
+ * of the lib they depend on. System/shell/root can access all shared
+ * libs regardless of dependency but need to explicitly ask for them
+ * via this flag.
+ * @hide
+ */
+ public static final int MATCH_STATIC_SHARED_LIBRARIES = 0x04000000;
+
+ /**
* Internal flag used to indicate that a system component has done their
* homework and verified that they correctly handle packages and components
* that come and go over time. In particular:
@@ -4618,6 +4630,14 @@
/**
* If there is already an application with the given package name installed
+ * on the system for other users, also install it for the calling user.
+ * @hide
+ */
+ public abstract int installExistingPackage(String packageName, @InstallReason int installReason)
+ throws NameNotFoundException;
+
+ /**
+ * If there is already an application with the given package name installed
* on the system for other users, also install it for the specified user.
* @hide
*/
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b5fd116..3eea72d 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -203,6 +203,8 @@
private static final String APK_DATA = "apk";
private static final String PROCESS_DATA = "pr";
private static final String CPU_DATA = "cpu";
+ private static final String GLOBAL_CPU_FREQ_DATA = "gcf";
+ private static final String CPU_TIMES_AT_FREQ_DATA = "ctf";
private static final String SENSOR_DATA = "sr";
private static final String VIBRATOR_DATA = "vib";
private static final String FOREGROUND_DATA = "fg";
@@ -265,6 +267,13 @@
private final Formatter mFormatter = new Formatter(mFormatBuilder);
/**
+ * Indicates times spent by the uid at each cpu frequency in all process states.
+ *
+ * Other types might include times spent in foreground, background etc.
+ */
+ private final String UID_TIMES_TYPE_ALL = "A";
+
+ /**
* State for keeping track of counting information.
*/
public static abstract class Counter {
@@ -303,6 +312,24 @@
}
/**
+ * State for keeping track of array of long counting information.
+ */
+ public static abstract class LongCounterArray {
+ /**
+ * Returns the counts associated with this Counter for the
+ * selected type of statistics.
+ *
+ * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
+ */
+ public abstract long[] getCountsLocked(int which);
+
+ /**
+ * Temporary for debugging.
+ */
+ public abstract void logState(Printer pw, String prefix);
+ }
+
+ /**
* Container class that aggregates counters for transmit, receive, and idle state of a
* radio controller.
*/
@@ -523,6 +550,9 @@
public abstract Timer getBluetoothScanBackgroundTimer();
public abstract Counter getBluetoothScanResultCounter();
+ public abstract long[] getCpuFreqTimes(int which);
+ public abstract long[] getScreenOffCpuFreqTimes(int which);
+
// 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.
@@ -1077,6 +1107,8 @@
public abstract long getNextMaxDailyDeadline();
+ public abstract long[] getCpuFreqs();
+
public final static class HistoryTag {
public String string;
public int uid;
@@ -3274,6 +3306,15 @@
}
}
+ final long[] cpuFreqs = getCpuFreqs();
+ if (cpuFreqs != null) {
+ sb.setLength(0);
+ for (int i = 0; i < cpuFreqs.length; ++i) {
+ sb.append((i == 0 ? "" : ",") + cpuFreqs[i]);
+ }
+ dumpLine(pw, 0 /* uid */, category, GLOBAL_CPU_FREQ_DATA, sb.toString());
+ }
+
for (int iu = 0; iu < NU; iu++) {
final int uid = uidStats.keyAt(iu);
if (reqUid >= 0 && uid != reqUid) {
@@ -3506,6 +3547,27 @@
0 /* old cpu power, keep for compatibility */);
}
+ final long[] cpuFreqTimeMs = u.getCpuFreqTimes(which);
+ // If total cpuFreqTimes is null, then we don't need to check for screenOffCpuFreqTimes.
+ if (cpuFreqTimeMs != null) {
+ sb.setLength(0);
+ for (int i = 0; i < cpuFreqTimeMs.length; ++i) {
+ sb.append((i == 0 ? "" : ",") + cpuFreqTimeMs[i]);
+ }
+ final long[] screenOffCpuFreqTimeMs = u.getScreenOffCpuFreqTimes(which);
+ if (screenOffCpuFreqTimeMs != null) {
+ for (int i = 0; i < screenOffCpuFreqTimeMs.length; ++i) {
+ sb.append("," + screenOffCpuFreqTimeMs[i]);
+ }
+ } else {
+ for (int i = 0; i < cpuFreqTimeMs.length; ++i) {
+ sb.append(",0");
+ }
+ }
+ dumpLine(pw, uid, category, CPU_TIMES_AT_FREQ_DATA, UID_TIMES_TYPE_ALL,
+ cpuFreqTimeMs.length, sb.toString());
+ }
+
final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
= u.getProcessStats();
for (int ipr=processStats.size()-1; ipr>=0; ipr--) {
@@ -4373,6 +4435,16 @@
pw.println(sb.toString());
}
+ final long[] cpuFreqs = getCpuFreqs();
+ if (cpuFreqs != null) {
+ sb.setLength(0);
+ sb.append("CPU freqs:");
+ for (int i = 0; i < cpuFreqs.length; ++i) {
+ sb.append(" " + cpuFreqs[i]);
+ }
+ pw.println(sb.toString());
+ }
+
for (int iu=0; iu<NU; iu++) {
final int uid = uidStats.keyAt(iu);
if (reqUid >= 0 && uid != reqUid && uid != Process.SYSTEM_UID) {
@@ -4835,6 +4907,25 @@
pw.println(sb.toString());
}
+ final long[] cpuFreqTimes = u.getCpuFreqTimes(which);
+ if (cpuFreqTimes != null) {
+ sb.setLength(0);
+ sb.append(" Total cpu time per freq:");
+ for (int i = 0; i < cpuFreqTimes.length; ++i) {
+ sb.append(" " + cpuFreqTimes[i]);
+ }
+ pw.println(sb.toString());
+ }
+ final long[] screenOffCpuFreqTimes = u.getScreenOffCpuFreqTimes(which);
+ if (screenOffCpuFreqTimes != null) {
+ sb.setLength(0);
+ sb.append(" Total screen-off cpu time per freq:");
+ for (int i = 0; i < screenOffCpuFreqTimes.length; ++i) {
+ sb.append(" " + screenOffCpuFreqTimes[i]);
+ }
+ pw.println(sb.toString());
+ }
+
final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
= u.getProcessStats();
for (int ipr=processStats.size()-1; ipr>=0; ipr--) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cf44c7d..3cd54b8 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6946,6 +6946,13 @@
public static final String CMAS_ADDITIONAL_BROADCAST_PKG = "cmas_additional_broadcast_pkg";
/**
+ * Whether the launcher should show any notification badges.
+ * The value is boolean (1 or 0).
+ * @hide
+ */
+ public static final String NOTIFICATION_BADGING = "notification_badging";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -7040,7 +7047,8 @@
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
ASSIST_GESTURE_ENABLED,
ASSIST_GESTURE_SENSITIVITY,
- VR_DISPLAY_MODE
+ VR_DISPLAY_MODE,
+ NOTIFICATION_BADGING
};
/**
@@ -9947,7 +9955,8 @@
CALL_AUTO_RETRY,
DOCK_AUDIO_MEDIA_ENABLED,
ENCODED_SURROUND_OUTPUT,
- LOW_POWER_MODE_TRIGGER_LEVEL
+ LOW_POWER_MODE_TRIGGER_LEVEL,
+ BLUETOOTH_ON
};
private static final ContentProviderHolder sProviderHolder =
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 5fd9458..9df315b 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -116,15 +116,7 @@
final IFillCallback callback = (IFillCallback) args.arg3;
final FillCallback fillCallback = new FillCallback(callback, request.getId());
args.recycle();
- // TODO(b/37563972): temporary try-catch hack to support old method
- try {
- onFillRequest(request, cancellation, fillCallback);
- } catch (AbstractMethodError e) {
- final List<FillContext> contexts = request.getFillContexts();
- onFillRequest(contexts.get(contexts.size() - 1).getStructure(),
- request.getClientState(), request.getFlags(), cancellation,
- fillCallback);
- }
+ onFillRequest(request, cancellation, fillCallback);
break;
} case MSG_ON_SAVE_REQUEST: {
final SomeArgs args = (SomeArgs) msg.obj;
@@ -132,14 +124,7 @@
final ISaveCallback callback = (ISaveCallback) args.arg2;
final SaveCallback saveCallback = new SaveCallback(callback);
args.recycle();
- // TODO(b/37563972): temporary try-catch hack to support old method
- try {
- onSaveRequest(request, saveCallback);
- } catch (AbstractMethodError e) {
- final List<FillContext> contexts = request.getFillContexts();
- onSaveRequest(contexts.get(contexts.size() - 1).getStructure(),
- request.getClientState(), saveCallback);
- }
+ onSaveRequest(request, saveCallback);
break;
} case MSG_DISCONNECT: {
onDisconnected();
@@ -201,37 +186,6 @@
@NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
/**
- * Called by the Android system do decide if an {@link Activity} can be autofilled by the
- * service.
- *
- * <p>Service must call one of the {@link FillCallback} methods (like
- * {@link FillCallback#onSuccess(FillResponse)}
- * or {@link FillCallback#onFailure(CharSequence)})
- * to notify the result of the request.
- *
- * @param structure {@link Activity}'s view structure.
- * @param data bundle containing data passed by the service in a last call to
- * {@link FillResponse.Builder#setExtras(Bundle)}, if any. This bundle allows your
- * service to keep state between fill and save requests as well as when filling different
- * sections of the UI as the system will try to aggressively unbind from the service to
- * conserve resources.
- * See {@link FillResponse} for examples of multiple-sections requests.
- * @param flags either {@code 0} or {@link AutofillManager#FLAG_MANUAL_REQUEST}.
- * @param cancellationSignal signal for observing cancellation requests. The system will use
- * this to notify you that the fill result is no longer needed and you should stop
- * handling this fill request in order to save resources.
- * @param callback object used to notify the result of the request.
- *
- * @hide
- */
- @Deprecated
- public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
- int flags, @NonNull CancellationSignal cancellationSignal,
- @NonNull FillCallback callback) {
-
- }
-
- /**
* Called when user requests service to save the fields of an {@link Activity}.
*
* <p>Service must call one of the {@link SaveCallback} methods (like
@@ -246,30 +200,6 @@
@NonNull SaveCallback callback);
/**
- * Called when user requests service to save the fields of an {@link Activity}.
- *
- * <p>Service must call one of the {@link SaveCallback} methods (like
- * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
- * to notify the result of the request.
- *
- * @param structure {@link Activity}'s view structure.
- * @param data bundle containing data passed by the service in a last call to
- * {@link FillResponse.Builder#setExtras(Bundle)}, if any. This bundle allows your
- * service to keep state between fill and save requests as well as when filling different
- * sections of the UI as the system will try to aggressively unbind from the service to
- * conserve resources.
- * See {@link FillResponse} for examples of multiple-sections requests.
- * @param callback object used to notify the result of the request.
- *
- * @hide
- */
- @Deprecated
- public void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
- @NonNull SaveCallback callback) {
-
- }
-
- /**
* Called when the Android system disconnects from the service.
*
* <p> At this point this service may no longer be an active {@link AutofillService}.
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 9487760..fa3f55b 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -326,15 +326,6 @@
return this;
}
- /** @hide */
- // TODO (b/37563972): Remove when callers migrate
- public @NonNull Builder setNegativeAction(@Nullable CharSequence title,
- @Nullable IntentSender listener) {
- throwIfDestroyed();
- setNegativeAction(NEGATIVE_BUTTON_STYLE_CANCEL, listener);
- return this;
- }
-
/**
* Sets the style and listener for the negative save action.
*
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 749cf08..589edbc 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -56,7 +56,7 @@
private final boolean mIsOwner;
private final long mMemoryAddr;
- private int mFd;
+ private int mFd = -1;
/**
* Creates a new instance.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 07258fc..02ac9bd 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17306,15 +17306,14 @@
* item in a list view.
*
* @return Returns a Parcelable object containing the view's current dynamic
- * state, or null if there is nothing interesting to save. The
- * default implementation returns null.
+ * state, or null if there is nothing interesting to save.
* @see #onRestoreInstanceState(android.os.Parcelable)
* @see #saveHierarchyState(android.util.SparseArray)
* @see #dispatchSaveInstanceState(android.util.SparseArray)
* @see #setSaveEnabled(boolean)
*/
@CallSuper
- protected Parcelable onSaveInstanceState() {
+ @Nullable protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (mStartActivityRequestWho != null || isAutofilled()
|| mAccessibilityViewId > LAST_APP_ACCESSIBILITY_ID) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 75a9965..68c74da 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -16,6 +16,7 @@
package android.view.autofill;
+import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
@@ -99,14 +100,6 @@
static final String SESSION_ID_TAG = "android:sessionId";
static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
- /**
- * @deprecated Use {@link android.service.autofill.FillRequest#FLAG_MANUAL_REQUEST}
- * @hide
- */
- // TODO(b/37563972): remove (and change value of private flags)
- @Deprecated
- public static final int FLAG_MANUAL_REQUEST = 0x1;
-
/** @hide */ public static final int ACTION_START_SESSION = 1;
/** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
/** @hide */ public static final int ACTION_VIEW_EXITED = 3;
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 1f3be84..32fae73 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -140,4 +140,14 @@
@WorkerThread
LinksInfo getLinks(
@NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales);
+
+ /**
+ * Logs a TextClassifier event.
+ *
+ * @param source the text classifier used to generate this event
+ * @param event the text classifier related event
+ * @hide
+ */
+ @WorkerThread
+ default void logEvent(String source, String event) {}
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 7362c70..5f72fc7 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -40,6 +40,7 @@
import android.widget.TextViewMetrics;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.Preconditions;
import java.io.File;
@@ -77,6 +78,8 @@
private final Context mContext;
+ private final MetricsLogger mMetricsLogger = new MetricsLogger();
+
private final Object mSmartSelectionLock = new Object();
@GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
private Map<Locale, String> mModelFilePaths;
@@ -105,7 +108,8 @@
if (start <= end
&& start >= 0 && end <= string.length()
&& start <= selectionStartIndex && end >= selectionEndIndex) {
- final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
+ final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end)
+ .setLogSource(LOG_TAG);
final SmartSelection.ClassificationResult[] results =
smartSelection.classifyText(
string, start, end,
@@ -173,6 +177,13 @@
return TextClassifier.NO_OP.getLinks(text, linkMask, defaultLocales);
}
+ @Override
+ public void logEvent(String source, String event) {
+ if (LOG_TAG.equals(source)) {
+ mMetricsLogger.count(event, 1);
+ }
+ }
+
private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
synchronized (mSmartSelectionLock) {
localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 3172c13..9a66693 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -34,13 +34,16 @@
private final int mEndIndex;
@NonNull private final EntityConfidence<String> mEntityConfidence;
@NonNull private final List<String> mEntities;
+ @NonNull private final String mLogSource;
private TextSelection(
- int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence) {
+ int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence,
+ @NonNull String logSource) {
mStartIndex = startIndex;
mEndIndex = endIndex;
mEntityConfidence = new EntityConfidence<>(entityConfidence);
mEntities = mEntityConfidence.getEntities();
+ mLogSource = logSource;
}
/**
@@ -87,6 +90,14 @@
return mEntityConfidence.getConfidenceScore(entity);
}
+ /**
+ * Returns a tag for the source classifier used to generate this result.
+ * @hide
+ */
+ public String getSourceClassifier() {
+ return mLogSource;
+ }
+
@Override
public String toString() {
return String.format("TextSelection {%d, %d, %s}",
@@ -102,6 +113,7 @@
private final int mEndIndex;
@NonNull private final EntityConfidence<String> mEntityConfidence =
new EntityConfidence<>();
+ @NonNull private String mLogSource = "";
/**
* Creates a builder used to build {@link TextSelection} objects.
@@ -131,10 +143,19 @@
}
/**
+ * Sets a tag for the source classifier used to generate this result.
+ * @hide
+ */
+ Builder setLogSource(@NonNull String logSource) {
+ mLogSource = Preconditions.checkNotNull(logSource);
+ return this;
+ }
+
+ /**
* Builds and returns {@link TextSelection} object.
*/
public TextSelection build() {
- return new TextSelection(mStartIndex, mEndIndex, mEntityConfidence);
+ return new TextSelection(mStartIndex, mEndIndex, mEntityConfidence, mLogSource);
}
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b0d6395..bb658c1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3925,6 +3925,8 @@
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ getSelectionActionModeHelper().onSelectionAction();
+
if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
return true;
}
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 16a1087..89182b0 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -56,13 +56,14 @@
private TextClassification mTextClassification;
private AsyncTask mTextClassificationAsyncTask;
- private final SelectionInfo mSelectionInfo = new SelectionInfo();
+ private final SelectionTracker mSelectionTracker;
SelectionActionModeHelper(@NonNull Editor editor) {
mEditor = Preconditions.checkNotNull(editor);
final TextView textView = mEditor.getTextView();
mTextClassificationHelper = new TextClassificationHelper(
textView.getTextClassifier(), textView.getText(), 0, 1, textView.getTextLocales());
+ mSelectionTracker = new SelectionTracker(textView.getTextClassifier());
}
public void startActionModeAsync(boolean adjustSelection) {
@@ -99,8 +100,13 @@
}
}
+ public void onSelectionAction() {
+ mSelectionTracker.onSelectionAction(mTextClassificationHelper.getClassifierTag());
+ }
+
public boolean resetSelection(int textIndex) {
- if (mSelectionInfo.resetSelection(textIndex, mEditor)) {
+ if (mSelectionTracker.resetSelection(
+ textIndex, mEditor, mTextClassificationHelper.getClassifierTag())) {
invalidateActionModeAsync();
return true;
}
@@ -113,7 +119,7 @@
}
public void onDestroyActionMode() {
- mSelectionInfo.onSelectionDestroyed();
+ mSelectionTracker.onSelectionDestroyed();
cancelAsyncTask();
}
@@ -137,7 +143,7 @@
private void startActionMode(@Nullable SelectionResult result) {
final TextView textView = mEditor.getTextView();
final CharSequence text = textView.getText();
- mSelectionInfo.setOriginalSelection(
+ mSelectionTracker.setOriginalSelection(
textView.getSelectionStart(), textView.getSelectionEnd());
if (result != null && text instanceof Spannable) {
Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
@@ -151,7 +157,8 @@
controller.show();
}
if (result != null) {
- mSelectionInfo.onSelectionStarted(result.mStart, result.mEnd);
+ mSelectionTracker.onSelectionStarted(
+ result.mStart, result.mEnd, mTextClassificationHelper.getClassifierTag());
}
}
mEditor.setRestartActionModeOnNextRefresh(false);
@@ -165,7 +172,9 @@
actionMode.invalidate();
}
final TextView textView = mEditor.getTextView();
- mSelectionInfo.onSelectionUpdated(textView.getSelectionStart(), textView.getSelectionEnd());
+ mSelectionTracker.onSelectionUpdated(
+ textView.getSelectionStart(), textView.getSelectionEnd(),
+ mTextClassificationHelper.getClassifierTag());
mTextClassificationAsyncTask = null;
}
@@ -177,49 +186,111 @@
}
/**
- * Holds information about the selection and uses it to decide on whether or not to update
- * the selection when resetSelection is called.
- * The expected UX here is to allow the user to select a word inside of the "smart selection" on
- * a single tap.
+ * Tracks and logs smart selection changes.
+ * It is important to trigger this object's methods at the appropriate event so that it tracks
+ * smart selection events appropriately.
*/
- private static final class SelectionInfo {
+ private static final class SelectionTracker {
+
+ // Log event: Smart selection happened.
+ private static final String LOG_EVENT_MULTI_SELECTION =
+ "textClassifier_multiSelection";
+
+ // Log event: Smart selection acted upon.
+ private static final String LOG_EVENT_MULTI_SELECTION_ACTION =
+ "textClassifier_multiSelection_action";
+
+ // Log event: Smart selection was reset to original selection.
+ private static final String LOG_EVENT_MULTI_SELECTION_RESET =
+ "textClassifier_multiSelection_reset";
+
+ // Log event: Smart selection was user modified.
+ private static final String LOG_EVENT_MULTI_SELECTION_MODIFIED =
+ "textClassifier_multiSelection_modified";
+
+ private final TextClassifier mClassifier;
private int mOriginalStart;
private int mOriginalEnd;
private int mSelectionStart;
private int mSelectionEnd;
- private boolean mResetOriginal;
+ private boolean mSmartSelectionActive;
+ SelectionTracker(TextClassifier classifier) {
+ mClassifier = classifier;
+ }
+
+ /**
+ * Called to initialize the original selection before smart selection is triggered.
+ */
public void setOriginalSelection(int selectionStart, int selectionEnd) {
mOriginalStart = selectionStart;
mOriginalEnd = selectionEnd;
- mResetOriginal = false;
+ mSmartSelectionActive = false;
}
- public void onSelectionStarted(int selectionStart, int selectionEnd) {
- // Set the reset flag to true if the selection changed.
+ /**
+ * Called when selection action mode is started.
+ * If the selection indices are different from the original selection indices, we have a
+ * smart selection.
+ */
+ public void onSelectionStarted(int selectionStart, int selectionEnd, String logTag) {
mSelectionStart = selectionStart;
mSelectionEnd = selectionEnd;
- mResetOriginal = mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
+ // If the started selection is different from the original selection, we have a
+ // smart selection.
+ mSmartSelectionActive =
+ mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
+ if (mSmartSelectionActive) {
+ mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION);
+ }
}
- public void onSelectionUpdated(int selectionStart, int selectionEnd) {
- // If the selection did not change, maintain the reset state. Otherwise, disable reset.
- mResetOriginal &= selectionStart == mSelectionStart && selectionEnd == mSelectionEnd;
+ /**
+ * Called when selection bounds change.
+ */
+ public void onSelectionUpdated(int selectionStart, int selectionEnd, String logTag) {
+ final boolean selectionChanged =
+ selectionStart != mSelectionStart || selectionEnd != mSelectionEnd;
+ if (selectionChanged) {
+ if (mSmartSelectionActive) {
+ mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_MODIFIED);
+ }
+ mSmartSelectionActive = false;
+ }
}
+ /**
+ * Called when the selection action mode is destroyed.
+ */
public void onSelectionDestroyed() {
- mResetOriginal = false;
+ mSmartSelectionActive = false;
}
- public boolean resetSelection(int textIndex, Editor editor) {
+ /**
+ * Logs if the action was taken on a smart selection.
+ */
+ public void onSelectionAction(String logTag) {
+ if (mSmartSelectionActive) {
+ mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_ACTION);
+ }
+ }
+
+ /**
+ * Returns true if the current smart selection should be reset to normal selection based on
+ * information that has been recorded about the original selection and the smart selection.
+ * The expected UX here is to allow the user to select a word inside of the smart selection
+ * on a single tap.
+ */
+ public boolean resetSelection(int textIndex, Editor editor, String logTag) {
final CharSequence text = editor.getTextView().getText();
- if (mResetOriginal
+ if (mSmartSelectionActive
&& textIndex >= mSelectionStart && textIndex <= mSelectionEnd
&& text instanceof Spannable) {
// Only allow a reset once.
- mResetOriginal = false;
+ mSmartSelectionActive = false;
+ mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_RESET);
return editor.selectCurrentWord();
}
return false;
@@ -301,6 +372,7 @@
/** End index relative to mText. */
private int mSelectionEnd;
private LocaleList mLocales;
+ private String mClassifierTag = "";
/** Trimmed text starting from mTrimStart in mText. */
private CharSequence mTrimmedText;
@@ -364,9 +436,14 @@
mTrimmedText, mRelativeStart, mRelativeEnd, mLocales);
mSelectionStart = Math.max(0, sel.getSelectionStartIndex() + mTrimStart);
mSelectionEnd = Math.min(mText.length(), sel.getSelectionEndIndex() + mTrimStart);
+ mClassifierTag = sel.getSourceClassifier();
return classifyText();
}
+ String getClassifierTag() {
+ return mClassifierTag;
+ }
+
private void trimText() {
mTrimStart = Math.max(0, mSelectionStart - TRIM_DELTA);
final int referenceEnd = Math.min(mText.length(), mSelectionEnd + TRIM_DELTA);
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index fef85da..797cf2b 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -40,6 +40,7 @@
public static String UPDATES = "UPDATES";
public static String NETWORK_STATUS = "NETWORK_STATUS";
public static String NETWORK_ALERTS = "NETWORK_ALERTS";
+ public static String NETWORK_AVAILABLE = "NETWORK_AVAILABLE";
public static String VPN = "VPN";
public static String DEVICE_ADMIN = "DEVICE_ADMIN";
public static String ALERTS = "ALERTS";
@@ -99,6 +100,11 @@
channelsList.add(networkAlertsChannel);
channelsList.add(new NotificationChannel(
+ NETWORK_AVAILABLE,
+ context.getString(R.string.notification_channel_network_available),
+ NotificationManager.IMPORTANCE_LOW));
+
+ channelsList.add(new NotificationChannel(
VPN,
context.getString(R.string.notification_channel_vpn),
NotificationManager.IMPORTANCE_LOW));
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d3eb451..0d0d099 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,6 +16,7 @@
package com.android.internal.os;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
@@ -87,6 +88,7 @@
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
@@ -114,7 +116,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 156 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 157 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -149,6 +151,8 @@
private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
+ private final KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader =
+ new KernelUidCpuFreqTimeReader();
private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
= new KernelMemoryBandwidthStats();
@@ -570,6 +574,8 @@
private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
+ private long[] mCpuFreqs;
+
private PowerProfile mPowerProfile;
/*
@@ -961,6 +967,131 @@
}
}
+ public static class LongSamplingCounterArray extends LongCounterArray implements TimeBaseObs {
+ final TimeBase mTimeBase;
+ long[] mCounts;
+ long[] mLoadedCounts;
+ long[] mUnpluggedCounts;
+ long[] mPluggedCounts;
+
+ LongSamplingCounterArray(TimeBase timeBase, Parcel in) {
+ mTimeBase = timeBase;
+ mPluggedCounts = in.createLongArray();
+ mCounts = copyArray(mPluggedCounts, mCounts);
+ mLoadedCounts = in.createLongArray();
+ mUnpluggedCounts = in.createLongArray();
+ timeBase.add(this);
+ }
+
+ LongSamplingCounterArray(TimeBase timeBase) {
+ mTimeBase = timeBase;
+ timeBase.add(this);
+ }
+
+ public void writeToParcel(Parcel out) {
+ out.writeLongArray(mCounts);
+ out.writeLongArray(mLoadedCounts);
+ out.writeLongArray(mUnpluggedCounts);
+ }
+
+ @Override
+ public void onTimeStarted(long elapsedRealTime, long baseUptime, long baseRealtime) {
+ mUnpluggedCounts = copyArray(mPluggedCounts, mUnpluggedCounts);
+ mCounts = copyArray(mPluggedCounts, mCounts);
+ }
+
+ @Override
+ public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
+ mPluggedCounts = copyArray(mCounts, mPluggedCounts);
+ }
+
+ @Override
+ public long[] getCountsLocked(int which) {
+ long[] val = copyArray(mTimeBase.isRunning() ? mCounts : mPluggedCounts, null);
+ if (which == STATS_SINCE_UNPLUGGED) {
+ subtract(val, mUnpluggedCounts);
+ } else if (which != STATS_SINCE_CHARGED) {
+ subtract(val, mLoadedCounts);
+ }
+ return val;
+ }
+
+ @Override
+ public void logState(Printer pw, String prefix) {
+ pw.println(prefix + "mCounts=" + Arrays.toString(mCounts)
+ + " mLoadedCounts=" + Arrays.toString(mLoadedCounts)
+ + " mUnpluggedCounts=" + Arrays.toString(mUnpluggedCounts)
+ + " mPluggedCounts=" + Arrays.toString(mPluggedCounts));
+ }
+
+ void addCountLocked(long[] counts) {
+ if (counts == null) {
+ return;
+ }
+ if (mCounts == null) {
+ mCounts = new long[counts.length];
+ }
+ for (int i = 0; i < counts.length; ++i) {
+ mCounts[i] += counts[i];
+ }
+ }
+
+ /**
+ * Clear state of this counter.
+ */
+ void reset(boolean detachIfReset) {
+ fillArray(mCounts, 0);
+ fillArray(mLoadedCounts, 0);
+ fillArray(mPluggedCounts, 0);
+ fillArray(mUnpluggedCounts, 0);
+ if (detachIfReset) {
+ detach();
+ }
+ }
+
+ void detach() {
+ mTimeBase.remove(this);
+ }
+
+ void writeSummaryFromParcelLocked(Parcel out) {
+ out.writeLongArray(mCounts);
+ }
+
+ void readSummaryFromParcelLocked(Parcel in) {
+ mCounts = in.createLongArray();
+ mLoadedCounts = copyArray(mCounts, mLoadedCounts);
+ mUnpluggedCounts = copyArray(mCounts, mUnpluggedCounts);
+ mPluggedCounts = copyArray(mCounts, mPluggedCounts);
+ }
+
+ private void fillArray(long[] a, long val) {
+ if (a != null) {
+ Arrays.fill(a, val);
+ }
+ }
+
+ private void subtract(@NonNull long[] val, long[] toSubtract) {
+ if (toSubtract == null) {
+ return;
+ }
+ for (int i = 0; i < val.length; i++) {
+ val[i] -= toSubtract[i];
+ }
+ }
+
+ private long[] copyArray(long[] src, long[] dest) {
+ if (src == null) {
+ return null;
+ } else {
+ if (dest == null) {
+ dest = new long[src.length];
+ }
+ System.arraycopy(src, 0, dest, 0, src.length);
+ return dest;
+ }
+ }
+ }
+
public static class LongSamplingCounter extends LongCounter implements TimeBaseObs {
final TimeBase mTimeBase;
long mCount;
@@ -5487,6 +5618,9 @@
LongSamplingCounter mSystemCpuTime;
LongSamplingCounter[][] mCpuClusterSpeed;
+ LongSamplingCounterArray mCpuFreqTimeMs;
+ LongSamplingCounterArray mScreenOffCpuFreqTimeMs;
+
/**
* The statistics we have collected for this uid's wake locks.
*/
@@ -5564,6 +5698,42 @@
}
@Override
+ public long[] getCpuFreqTimes(int which) {
+ if (mCpuFreqTimeMs == null) {
+ return null;
+ }
+ final long[] cpuFreqTimes = mCpuFreqTimeMs.getCountsLocked(which);
+ if (cpuFreqTimes == null) {
+ return null;
+ }
+ // Return cpuFreqTimes only if atleast one of the elements in non-zero.
+ for (int i = 0; i < cpuFreqTimes.length; ++i) {
+ if (cpuFreqTimes[i] != 0) {
+ return cpuFreqTimes;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public long[] getScreenOffCpuFreqTimes(int which) {
+ if (mScreenOffCpuFreqTimeMs == null) {
+ return null;
+ }
+ final long[] cpuFreqTimes = mScreenOffCpuFreqTimeMs.getCountsLocked(which);
+ if (cpuFreqTimes == null) {
+ return null;
+ }
+ // Return cpuFreqTimes only if atleast one of the elements in non-zero.
+ for (int i = 0; i < cpuFreqTimes.length; ++i) {
+ if (cpuFreqTimes[i] != 0) {
+ return cpuFreqTimes;
+ }
+ }
+ return null;
+ }
+
+ @Override
public ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> getWakelockStats() {
return mWakelockStats.getMap();
}
@@ -6358,6 +6528,13 @@
}
}
+ if (mCpuFreqTimeMs != null) {
+ mCpuFreqTimeMs.reset(false);
+ }
+ if (mScreenOffCpuFreqTimeMs != null) {
+ mScreenOffCpuFreqTimeMs.reset(false);
+ }
+
resetLongCounterIfNotNull(mMobileRadioApWakeupCount, false);
resetLongCounterIfNotNull(mWifiRadioApWakeupCount, false);
@@ -6527,6 +6704,13 @@
}
}
+ if (mCpuFreqTimeMs != null) {
+ mCpuFreqTimeMs.detach();
+ }
+ if (mScreenOffCpuFreqTimeMs != null) {
+ mScreenOffCpuFreqTimeMs.detach();
+ }
+
detachLongCounterIfNotNull(mMobileRadioApWakeupCount);
detachLongCounterIfNotNull(mWifiRadioApWakeupCount);
}
@@ -6743,6 +6927,19 @@
out.writeInt(0);
}
+ if (mCpuFreqTimeMs != null) {
+ out.writeInt(1);
+ mCpuFreqTimeMs.writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ }
+ if (mScreenOffCpuFreqTimeMs != null) {
+ out.writeInt(1);
+ mScreenOffCpuFreqTimeMs.writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ }
+
if (mMobileRadioApWakeupCount != null) {
out.writeInt(1);
mMobileRadioApWakeupCount.writeToParcel(out);
@@ -6991,6 +7188,18 @@
}
if (in.readInt() != 0) {
+ mCpuFreqTimeMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase, in);
+ } else {
+ mCpuFreqTimeMs = null;
+ }
+ if (in.readInt() != 0) {
+ mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray(
+ mBsi.mOnBatteryScreenOffTimeBase, in);
+ } else {
+ mScreenOffCpuFreqTimeMs = null;
+ }
+
+ if (in.readInt() != 0) {
mMobileRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
} else {
mMobileRadioApWakeupCount = null;
@@ -8196,6 +8405,10 @@
}
}
+ public long[] getCpuFreqs() {
+ return mCpuFreqs;
+ }
+
public BatteryStatsImpl(File systemDir, Handler handler, ExternalStatsSync externalSync) {
this(new SystemClocks(), systemDir, handler, externalSync, null);
}
@@ -9816,6 +10029,8 @@
}
});
+ readKernelUidCpuFreqTimesLocked();
+
final long elapse = (mClocks.elapsedRealtime() - startTimeMs);
if (DEBUG_ENERGY_CPU || (elapse >= 100)) {
Slog.d(TAG, "Reading cpu stats took " + elapse + " ms");
@@ -9904,6 +10119,30 @@
}
}
+ void readKernelUidCpuFreqTimesLocked() {
+ mKernelUidCpuFreqTimeReader.readDelta(!mOnBatteryInternal ? null :
+ new KernelUidCpuFreqTimeReader.Callback() {
+ @Override
+ public void onCpuFreqs(long[] cpuFreqs) {
+ mCpuFreqs = cpuFreqs;
+ }
+
+ @Override
+ public void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs) {
+ final Uid u = getUidStatsLocked(uid);
+ if (u.mCpuFreqTimeMs == null) {
+ u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase);
+ }
+ u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs);
+ if (u.mScreenOffCpuFreqTimeMs == null) {
+ u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray(
+ mOnBatteryScreenOffTimeBase);
+ }
+ u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs);
+ }
+ });
+ }
+
boolean setChargingLocked(boolean charging) {
if (mCharging != charging) {
mCharging = charging;
@@ -10992,6 +11231,8 @@
}
}
+ mCpuFreqs = in.createLongArray();
+
final int NU = in.readInt();
if (NU > 10000) {
throw new ParcelFormatException("File corrupt: too many uids " + NU);
@@ -11115,6 +11356,20 @@
}
if (in.readInt() != 0) {
+ u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase);
+ u.mCpuFreqTimeMs.readSummaryFromParcelLocked(in);
+ } else {
+ u.mCpuFreqTimeMs = null;
+ }
+ if (in.readInt() != 0) {
+ u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray(
+ mOnBatteryScreenOffTimeBase);
+ u.mScreenOffCpuFreqTimeMs.readSummaryFromParcelLocked(in);
+ } else {
+ u.mScreenOffCpuFreqTimeMs = null;
+ }
+
+ if (in.readInt() != 0) {
u.mMobileRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase);
u.mMobileRadioApWakeupCount.readSummaryFromParcelLocked(in);
} else {
@@ -11364,6 +11619,8 @@
}
}
+ out.writeLongArray(mCpuFreqs);
+
final int NU = mUidStats.size();
out.writeInt(NU);
for (int iu = 0; iu < NU; iu++) {
@@ -11508,6 +11765,19 @@
out.writeInt(0);
}
+ if (u.mCpuFreqTimeMs != null) {
+ out.writeInt(1);
+ u.mCpuFreqTimeMs.writeSummaryFromParcelLocked(out);
+ } else {
+ out.writeInt(0);
+ }
+ if (u.mScreenOffCpuFreqTimeMs != null) {
+ out.writeInt(1);
+ u.mScreenOffCpuFreqTimeMs.writeSummaryFromParcelLocked(out);
+ } else {
+ out.writeInt(0);
+ }
+
if (u.mMobileRadioApWakeupCount != null) {
out.writeInt(1);
u.mMobileRadioApWakeupCount.writeSummaryFromParcelLocked(out);
@@ -11798,6 +12068,8 @@
mFlashlightTurnedOnTimers.clear();
mCameraTurnedOnTimers.clear();
+ mCpuFreqs = in.createLongArray();
+
int numUids = in.readInt();
mUidStats.clear();
for (int i = 0; i < numUids; i++) {
@@ -11957,6 +12229,8 @@
}
}
+ out.writeLongArray(mCpuFreqs);
+
if (inclUids) {
int size = mUidStats.size();
out.writeInt(size);
diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
new file mode 100644
index 0000000..568c883
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
@@ -0,0 +1,117 @@
+/*
+ * 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 android.annotation.Nullable;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * Reads /proc/uid_time_in_state which has the format:
+ *
+ * uid: [freq1] [freq2] [freq3] ...
+ * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
+ * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
+ * ...
+ *
+ * This provides the times a UID's processes spent executing at each different cpu frequency.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+ * delta.
+ */
+public class KernelUidCpuFreqTimeReader {
+ private static final String TAG = "KernelUidCpuFreqTimeReader";
+ private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
+
+ public interface Callback {
+ void onCpuFreqs(long[] cpuFreqs);
+ void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs);
+ }
+
+ private long[] mCpuFreqs;
+ private int mCpuFreqsCount;
+
+ private SparseArray<long[]> mLastUidCpuFreqTimeMs = new SparseArray<>();
+
+ public void readDelta(@Nullable Callback callback) {
+ try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
+ readDelta(reader, callback);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+ }
+ }
+
+ @VisibleForTesting
+ public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException {
+ String line = reader.readLine();
+ if (line == null) {
+ return;
+ }
+ readCpuFreqs(line, callback);
+ while ((line = reader.readLine()) != null) {
+ final int index = line.indexOf(' ');
+ final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
+ readTimesForUid(uid, line.substring(index + 1, line.length()), callback);
+ }
+ }
+
+ private void readTimesForUid(int uid, String line, Callback callback) {
+ long[] uidTimeMs = mLastUidCpuFreqTimeMs.get(uid);
+ if (uidTimeMs == null) {
+ uidTimeMs = new long[mCpuFreqsCount];
+ mLastUidCpuFreqTimeMs.put(uid, uidTimeMs);
+ }
+ final String[] timesStr = line.split(" ");
+ final int size = timesStr.length;
+ if (size != uidTimeMs.length) {
+ Slog.e(TAG, "No. of readings don't match cpu freqs, readings: " + size
+ + " cpuFreqsCount: " + uidTimeMs.length);
+ return;
+ }
+ final long[] deltaUidTimeMs = new long[size];
+ for (int i = 0; i < size; ++i) {
+ // Times read will be in units of 10ms
+ final long totalTimeMs = Long.parseLong(timesStr[i], 10) * 10;
+ deltaUidTimeMs[i] = totalTimeMs - uidTimeMs[i];
+ uidTimeMs[i] = totalTimeMs;
+ }
+ if (callback != null) {
+ callback.onUidCpuFreqTime(uid, deltaUidTimeMs);
+ }
+ }
+
+ private void readCpuFreqs(String line, Callback callback) {
+ if (mCpuFreqs == null) {
+ final String[] freqStr = line.split(" ");
+ // First item would be "uid:" which needs to be ignored
+ mCpuFreqsCount = freqStr.length - 1;
+ mCpuFreqs = new long[mCpuFreqsCount];
+ for (int i = 0; i < mCpuFreqsCount; ++i) {
+ mCpuFreqs[i] = Long.parseLong(freqStr[i + 1], 10);
+ }
+ }
+ if (callback != null) {
+ callback.onCpuFreqs(mCpuFreqs);
+ }
+ }
+}
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index ea40fd5..8b73daf 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -503,6 +503,7 @@
SettingProto demo_user_setup_complete = 165;
SettingProto instant_apps_enabled = 166;
SettingProto device_paired = 167;
+ SettingProto notification_badging = 168;
}
message SystemSettingsProto {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b7e8467..6640102 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1013,6 +1013,9 @@
<!-- Is the notification LED intrusive? Used to decide if there should be a disable option -->
<bool name="config_intrusiveNotificationLed">false</bool>
+ <!-- De we do icon badges? Used to decide if there should be a disable option-->
+ <bool name="config_notificationBadging">true</bool>
+
<!-- Default value for LED off time when the battery is low on charge in miliseconds -->
<integer name="config_notificationsBatteryLedOff">2875</integer>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cb1851b..98356a2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -629,6 +629,9 @@
<!-- Text shown when viewing channel settings for notifications related to network alerts -->
<string name="notification_channel_network_alerts">Network alerts</string>
+ <!-- Text shown when viewing the channel settings for notification about open nearby wireless networks. -->
+ <string name="notification_channel_network_available">Network available</string>
+
<!-- Text shown when viewing channel settings for notifications related to vpn status -->
<string name="notification_channel_vpn">VPN status</string>
@@ -2993,6 +2996,17 @@
<!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, t his is shown instead. For example, if the ringtone was on a SD card and it had been removed, this woudl be shown for ringtones on that SD card. -->
<string name="ringtone_unknown">Unknown</string>
+ <!-- A notification is shown when there are open wireless networks nearby. This is the notification's title. -->
+ <plurals name="wifi_available">
+ <item quantity="one">Wi-Fi network available</item>
+ <item quantity="other">Wi-Fi networks available</item>
+ </plurals>
+ <!-- A notification is shown when there are open wireless networks nearby. This is the notification's message. -->
+ <plurals name="wifi_available_detailed">
+ <item quantity="one">Open Wi-Fi network available</item>
+ <item quantity="other">Open Wi-Fi networks available</item>
+ </plurals>
+
<!-- A notification is shown when a wifi captive portal network is detected. This is the notification's title. -->
<string name="wifi_available_sign_in">Sign in to Wi-Fi network</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 31d13c9..74779f3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1861,6 +1861,8 @@
<java-symbol type="layout" name="safe_mode" />
<java-symbol type="layout" name="simple_list_item_2_single_choice" />
<java-symbol type="layout" name="app_error_dialog" />
+ <java-symbol type="plurals" name="wifi_available" />
+ <java-symbol type="plurals" name="wifi_available_detailed" />
<java-symbol type="string" name="accessibility_binding_label" />
<java-symbol type="string" name="adb_active_notification_message" />
<java-symbol type="string" name="adb_active_notification_title" />
@@ -2087,6 +2089,7 @@
<java-symbol type="string" name="config_wifi_tether_enable" />
<java-symbol type="integer" name="config_wifi_wakeup_available" />
<java-symbol type="bool" name="config_intrusiveNotificationLed" />
+ <java-symbol type="bool" name="config_notificationBadging" />
<java-symbol type="dimen" name="preference_fragment_padding_bottom" />
<java-symbol type="dimen" name="preference_fragment_padding_side" />
<java-symbol type="drawable" name="expander_ic_maximized" />
@@ -2969,6 +2972,7 @@
<java-symbol type="string" name="notification_channel_updates" />
<java-symbol type="string" name="notification_channel_network_status" />
<java-symbol type="string" name="notification_channel_network_alerts" />
+ <java-symbol type="string" name="notification_channel_network_available" />
<java-symbol type="string" name="notification_channel_vpn" />
<java-symbol type="string" name="notification_channel_device_admin" />
<java-symbol type="string" name="notification_channel_alerts" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index ee2f1ca..54d5285 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -37,8 +37,6 @@
/** Tests that ensure appropriate settings are backed up. */
@RunWith(AndroidJUnit4.class)
-// TODO(b/37684646): Can re-enable pre-submit once test is fixed.
-//@Presubmit
@SmallTest
public class SettingsBackupTest {
@@ -167,6 +165,7 @@
Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH,
Settings.Global.BATTERY_SAVER_CONSTANTS,
Settings.Global.DEVICE_NAME,
+ Settings.Global.DEVICE_POLICY_CONSTANTS,
Settings.Global.DEVICE_PROVISIONED,
Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED,
Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
@@ -379,6 +378,7 @@
Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED,
Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED,
+ Settings.Global.WIFI_WAKEUP_AVAILABLE,
Settings.Global.WIFI_WATCHDOG_ON,
Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON,
Settings.Global.WINDOW_ANIMATION_SCALE,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
new file mode 100644
index 0000000..ad8221b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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 org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedReader;
+
+/**
+ * Test class for {@link KernelUidCpuFreqTimeReader}.
+ *
+ * To run the tests, use
+ *
+ * runtest -c com.android.internal.os.KernelUidCpuFreqTimeReaderTest frameworks-core
+ *
+ * or the following steps:
+ *
+ * Build: m FrameworksCoreTests
+ * Install: adb install -r \
+ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+ * Run: adb shell am instrument -e class com.android.internal.os.KernelUidCpuFreqTimeReaderTest -w \
+ * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelUidCpuFreqTimeReaderTest {
+ @Mock private BufferedReader mBufferedReader;
+ @Mock private KernelUidCpuFreqTimeReader.Callback mCallback;
+
+ private KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mKernelUidCpuFreqTimeReader = new KernelUidCpuFreqTimeReader();
+ }
+
+ @Test
+ public void testReadDelta() throws Exception {
+ final long[] freqs = {1, 12, 123, 1234, 12345, 123456};
+ final int[] uids = {1, 22, 333, 4444, 5555};
+ final long[][] times = new long[uids.length][freqs.length];
+ for (int i = 0; i < uids.length; ++i) {
+ for (int j = 0; j < freqs.length; ++j) {
+ times[i][j] = uids[i] * freqs[j] * 10;
+ }
+ }
+ final String[] uidsTimesLines = getUidTimesLines(uids, times);
+ final String[] lines = new String[uidsTimesLines.length + 1];
+ System.arraycopy(uidsTimesLines, 0, lines, 0, uidsTimesLines.length);
+ lines[uidsTimesLines.length] = null;
+ when(mBufferedReader.readLine())
+ .thenReturn(getFreqsLine(freqs), lines);
+ mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
+ verify(mCallback).onCpuFreqs(freqs);
+ for (int i = 0; i < uids.length; ++i) {
+ verify(mCallback).onUidCpuFreqTime(uids[i], times[i]);
+ }
+ verifyNoMoreInteractions(mCallback);
+ }
+
+ private String getFreqsLine(long[] freqs) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("uid:");
+ for (int i = 0; i < freqs.length; ++i) {
+ sb.append(" " + freqs[i]);
+ }
+ return sb.toString();
+ }
+
+ private String[] getUidTimesLines(int[] uids, long[][] times) {
+ final String[] lines = new String[uids.length];
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < uids.length; ++i) {
+ sb.setLength(0);
+ sb.append(uids[i] + ":");
+ for (int j = 0; j < times[i].length; ++j) {
+ sb.append(" " + times[i][j] / 10);
+ }
+ lines[i] = sb.toString();
+ }
+ return lines;
+ }
+}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 40d36aa..d586db4 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1879,15 +1879,22 @@
}
/**
- * Rebuilds any caches associated with the bitmap that are used for
- * drawing it. In the case of purgeable bitmaps, this call will attempt to
- * ensure that the pixels have been decoded.
- * If this is called on more than one bitmap in sequence, the priority is
- * given in LRU order (i.e. the last bitmap called will be given highest
- * priority).
+ * Builds caches associated with the bitmap that are used for drawing it.
*
- * For bitmaps with no associated caches, this call is effectively a no-op,
- * and therefore is harmless.
+ * <p>Starting in {@link android.os.Build.VERSION_CODES#N}, this call initiates an asynchronous
+ * upload to the GPU on RenderThread, if the Bitmap is not already uploaded. With Hardware
+ * Acceleration, Bitmaps must be uploaded to the GPU in order to be rendered. This is done by
+ * default the first time a Bitmap is drawn, but the process can take several milliseconds,
+ * depending on the size of the Bitmap. Each time a Bitmap is modified and drawn again, it must
+ * be re-uploaded.</p>
+ *
+ * <p>Calling this method in advance can save time in the first frame it's used. For example, it
+ * is recommended to call this on an image decoding worker thread when a decoded Bitmap is about
+ * to be displayed. It is recommended to make any pre-draw modifications to the Bitmap before
+ * calling this method, so the cached, uploaded copy may be reused without re-uploading.</p>
+ *
+ * In {@link android.os.Build.VERSION_CODES#KITKAT} and below, for purgeable bitmaps, this call
+ * would attempt to ensure that the pixels have been decoded.
*/
public void prepareToDraw() {
checkRecycled("Can't prepareToDraw on a recycled bitmap!");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index d1d59b2..1e171d3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -62,11 +62,12 @@
*/
private static final ArraySet<String> sBroadcastOnRestore;
static {
- sBroadcastOnRestore = new ArraySet<String>(4);
+ sBroadcastOnRestore = new ArraySet<String>(5);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_INPUT_METHODS);
+ sBroadcastOnRestore.add(Settings.Global.BLUETOOTH_ON);
}
private interface SettingsLookup {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index f475361..9309359 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1434,6 +1434,9 @@
dumpSetting(s, p,
Settings.Secure.DEVICE_PAIRED,
SecureSettingsProto.DEVICE_PAIRED);
+ dumpSetting(s, p,
+ Settings.Secure.NOTIFICATION_BADGING,
+ SecureSettingsProto.NOTIFICATION_BADGING);
}
private static void dumpProtoSystemSettingsLocked(
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 290ce1f..5460304 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -58,6 +58,7 @@
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+ <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
<uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
<uses-permission android:name="android.permission.REQUEST_NETWORK_SCORES" />
@@ -300,7 +301,6 @@
android:excludeFromRecents="true"
android:stateNotNeeded="true"
android:resumeWhilePausing="true"
- android:screenOrientation="behind"
android:resizeableActivity="true"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
android:theme="@style/RecentsTheme.Wallpaper">
diff --git a/packages/SystemUI/res/drawable/qs_background_primary.xml b/packages/SystemUI/res/drawable/qs_background_primary.xml
index 0bdbc5f..8ea9e06 100644
--- a/packages/SystemUI/res/drawable/qs_background_primary.xml
+++ b/packages/SystemUI/res/drawable/qs_background_primary.xml
@@ -15,6 +15,6 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="?android:attr/colorPrimaryDark"/>
+ <solid android:color="?android:attr/colorPrimary"/>
</shape>
</inset>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 9ab8ac6..cbc2575 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -21,6 +21,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
+ android:elevation="4dp"
android:background="@drawable/qs_customizer_background"
android:gravity="center_horizontal">
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 1c087b3..f41c494 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -20,6 +20,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/qs_detail_background"
+ android:elevation="4dp"
android:clickable="true"
android:orientation="vertical"
android:paddingBottom="8dp"
diff --git a/packages/SystemUI/res/layout/qs_footer.xml b/packages/SystemUI/res/layout/qs_footer.xml
index 047f7aa..c92c811 100644
--- a/packages/SystemUI/res/layout/qs_footer.xml
+++ b/packages/SystemUI/res/layout/qs_footer.xml
@@ -21,6 +21,7 @@
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="48dp"
+ android:elevation="4dp"
android:baselineAligned="false"
android:clickable="false"
android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 3658313..fb47bbc 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -14,22 +14,28 @@
limitations under the License.
-->
<com.android.systemui.qs.QSContainerImpl
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/quick_settings_container"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/quick_settings_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/colorPrimaryDark"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <View
+ android:id="@+id/qs_background"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:background="@drawable/qs_background_primary"
- android:clipToPadding="false"
- android:clipChildren="false"
- android:elevation="4dp">
+ android:elevation="4dp" />
<com.android.systemui.qs.QSPanel
- android:id="@+id/quick_settings_panel"
- android:background="#0000"
- android:layout_marginTop="28dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="48dp" />
+ android:id="@+id/quick_settings_panel"
+ android:layout_marginTop="28dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:elevation="4dp"
+ android:layout_marginBottom="48dp" />
<include layout="@layout/quick_status_bar_expanded_header" />
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 520dab4..65344b7 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -24,6 +24,7 @@
android:layout_height="@dimen/status_bar_header_height"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:baselineAligned="false"
+ android:elevation="4dp"
android:clickable="false"
android:clipChildren="false"
android:clipToPadding="false"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 63abee7..c00b562 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -775,4 +775,6 @@
<dimen name="signal_icon_size">17dp</dimen>
+ <dimen name="qs_gutter_height">6dp</dimen>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 88e8b39..2504e36 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -675,9 +675,14 @@
mLockPatternUtils = new LockPatternUtils(mContext);
KeyguardUpdateMonitor.setCurrentUser(ActivityManager.getCurrentUser());
- // Assume keyguard is showing (unless it's disabled) until we know for sure...
- setShowingLocked(!shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser()), true /* forceCallbacks */);
+ // Assume keyguard is showing (unless it's disabled) until we know for sure, unless Keyguard
+ // is disabled.
+ if (mContext.getResources().getBoolean(
+ com.android.keyguard.R.bool.config_enableKeyguardService)) {
+ setShowingLocked(!shouldWaitForProvisioning()
+ && !mLockPatternUtils.isLockScreenDisabled(
+ KeyguardUpdateMonitor.getCurrentUser()), true /* forceCallbacks */);
+ }
mStatusBarKeyguardViewManager =
SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index bdc0871..da2d38f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -73,7 +73,8 @@
mTouchHandler.onActivityPinned();
mMediaController.onActivityPinned();
mMenuController.onActivityPinned();
- mNotificationController.onActivityPinned(packageName);
+ mNotificationController.onActivityPinned(packageName,
+ true /* deferUntilAnimationEnds */);
SystemServicesProxy.getInstance(mContext).setPipVisibility(true);
}
@@ -104,6 +105,7 @@
mTouchHandler.setTouchEnabled(true);
mTouchHandler.onPinnedStackAnimationEnded();
mMenuController.onPinnedStackAnimationEnded();
+ mNotificationController.onPinnedStackAnimationEnded();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
index 53746e2..696fdbc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
@@ -60,6 +60,9 @@
private PipMotionHelper mMotionHelper;
+ // Used when building a deferred notification
+ private String mDeferredNotificationPackageName;
+
private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() {
@Override
public void onOpChanged(String op, String packageName) {
@@ -87,10 +90,47 @@
mMotionHelper = motionHelper;
}
- public void onActivityPinned(String packageName) {
+ public void onActivityPinned(String packageName, boolean deferUntilAnimationEnds) {
// Clear any existing notification
mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
+ if (deferUntilAnimationEnds) {
+ mDeferredNotificationPackageName = packageName;
+ } else {
+ showNotificationForApp(mDeferredNotificationPackageName);
+ }
+
+ // Register for changes to the app ops setting for this package while it is in PiP
+ registerAppOpsListener(packageName);
+ }
+
+ public void onPinnedStackAnimationEnded() {
+ if (mDeferredNotificationPackageName != null) {
+ showNotificationForApp(mDeferredNotificationPackageName);
+ mDeferredNotificationPackageName = null;
+ }
+ }
+
+ public void onActivityUnpinned(ComponentName topPipActivity) {
+ // Unregister for changes to the previously PiP'ed package
+ unregisterAppOpsListener();
+
+ // Reset the deferred notification package
+ mDeferredNotificationPackageName = null;
+
+ if (topPipActivity != null) {
+ // onActivityUnpinned() is only called after the transition is complete, so we don't
+ // need to defer until the animation ends to update the notification
+ onActivityPinned(topPipActivity.getPackageName(), false /* deferUntilAnimationEnds */);
+ } else {
+ mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
+ }
+ }
+
+ /**
+ * Builds and shows the notification for the given app.
+ */
+ private void showNotificationForApp(String packageName) {
// Build a new notification
final Notification.Builder builder =
new Notification.Builder(mContext, NotificationChannels.GENERAL)
@@ -105,20 +145,6 @@
// Show the new notification
mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
}
-
- // Register for changes to the app ops setting for this package while it is in PiP
- registerAppOpsListener(packageName);
- }
-
- public void onActivityUnpinned(ComponentName topPipActivity) {
- // Unregister for changes to the previously PiP'ed package
- unregisterAppOpsListener();
-
- if (topPipActivity != null) {
- onActivityPinned(topPipActivity.getPackageName());
- } else {
- mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index 727eb5a..5b54d85 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -44,6 +44,7 @@
*/
public class PipNotification {
private static final String TAG = "PipNotification";
+ private static final String NOTIFICATION_TAG = PipNotification.class.getName();
private static final boolean DEBUG = PipManager.DEBUG;
private static final String ACTION_MENU = "PipNotification.menu";
@@ -56,7 +57,7 @@
private MediaController mMediaController;
private String mDefaultTitle;
- private Icon mDefaultIcon;
+ private int mDefaultIconResId;
private boolean mNotified;
private String mTitle;
@@ -167,9 +168,8 @@
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);
+ mDefaultIconResId = res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR
+ ? R.drawable.pip_expand_ll : R.drawable.pip_expand_lr;
if (mNotified) {
// update notification
notifyPipNotification();
@@ -181,12 +181,16 @@
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())
+ .setSmallIcon(mDefaultIconResId)
.setContentTitle(!TextUtils.isEmpty(mTitle) ? mTitle : mDefaultTitle);
- mNotificationManager.notify(SystemMessage.NOTE_TV_PIP, mNotificationBuilder.build());
+ if (mArt != null) {
+ mNotificationBuilder.setStyle(new Notification.BigPictureStyle()
+ .bigPicture(mArt));
+ } else {
+ mNotificationBuilder.setStyle(null);
+ }
+ mNotificationManager.notify(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP,
+ mNotificationBuilder.build());
}
private void dismissPipNotification() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 06264ba..189c04c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -17,11 +17,14 @@
package com.android.systemui.qs;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.qs.customize.QSCustomizer;
@@ -39,6 +42,8 @@
protected float mQsExpansion;
private QSCustomizer mQSCustomizer;
private QSFooter mQSFooter;
+ private int mGutterHeight;
+ private View mBackground;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -52,6 +57,8 @@
mHeader = findViewById(R.id.header);
mQSCustomizer = findViewById(R.id.qs_customize);
mQSFooter = findViewById(R.id.qs_footer);
+ mBackground = findViewById(R.id.qs_background);
+ mGutterHeight = getContext().getResources().getDimensionPixelSize(R.dimen.qs_gutter_height);
}
@Override
@@ -94,8 +101,9 @@
public void updateBottom() {
int height = calculateContainerHeight();
- setBottom(getTop() + height);
+ setBottom(getTop() + height + mGutterHeight);
mQSDetail.setBottom(getTop() + height);
+ mBackground.setBottom(mQSDetail.getBottom());
// Pin QS Footer to the bottom of the panel.
mQSFooter.setTranslationY(height - mQSFooter.getHeight());
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 406f107..5cf049a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -32,6 +32,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.R.id;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
@@ -61,6 +62,7 @@
private QSContainerImpl mContainer;
private int mLayoutDirection;
private QSFooter mFooter;
+ private int mGutterHeight;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -75,7 +77,8 @@
mQSDetail = view.findViewById(R.id.qs_detail);
mHeader = view.findViewById(R.id.header);
mFooter = view.findViewById(R.id.qs_footer);
- mContainer = (QSContainerImpl) view;
+ mContainer = view.findViewById(id.quick_settings_container);
+ mGutterHeight = getContext().getResources().getDimensionPixelSize(R.dimen.qs_gutter_height);
mQSDetail.setQsPanel(mQSPanel, mHeader);
@@ -239,7 +242,8 @@
mContainer.setExpansion(expansion);
final float translationScaleY = expansion - 1;
if (!mHeaderAnimating) {
- getView().setTranslationY(mKeyguardShowing ? (translationScaleY * mHeader.getHeight())
+ int height = mHeader.getHeight() + mGutterHeight;
+ getView().setTranslationY(mKeyguardShowing ? (translationScaleY * height)
: headerTranslation);
}
mHeader.setExpansion(mKeyguardShowing ? 1 : expansion);
@@ -321,19 +325,19 @@
LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
int panelHeight = layoutParams.topMargin + layoutParams.bottomMargin +
+ mQSPanel.getMeasuredHeight();
- return panelHeight + getView().getPaddingBottom();
+ return panelHeight + getView().getPaddingBottom() + mGutterHeight;
} else {
- return getView().getMeasuredHeight();
+ return getView().getMeasuredHeight() + mGutterHeight;
}
}
@Override
public void setHeightOverride(int desiredHeight) {
- mContainer.setHeightOverride(desiredHeight);
+ mContainer.setHeightOverride(desiredHeight - mGutterHeight);
}
public int getQsMinExpansionHeight() {
- return mHeader.getHeight();
+ return mHeader.getHeight() + mGutterHeight;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 32af230..d12b16b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -340,10 +340,9 @@
switch (state) {
case Tile.STATE_UNAVAILABLE:
return Utils.getDisabled(context,
- Utils.getColorAttr(context, android.R.attr.textColorPrimary));
- case Tile.STATE_INACTIVE:
- return Utils.getDisabled(context,
Utils.getColorAttr(context, android.R.attr.colorForeground));
+ case Tile.STATE_INACTIVE:
+ return Utils.getColorAttr(context, android.R.attr.textColorHint);
case Tile.STATE_ACTIVE:
return Utils.getColorAttr(context, android.R.attr.textColorPrimary);
default:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index f5718d9..bae6a27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -23,6 +23,7 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
@@ -177,6 +178,7 @@
* Similar to mDimmed but is also true if it's not dimmable but should be
*/
private boolean mNeedsDimming;
+ private int mDimmedAlpha;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -214,6 +216,8 @@
mBackgroundDimmed = findViewById(R.id.backgroundDimmed);
mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg);
mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
+ mDimmedAlpha = Color.alpha(mContext.getColor(
+ R.color.notification_material_background_dimmed_color));
updateBackground();
updateBackgroundTint();
updateOutlineAlpha();
@@ -492,10 +496,21 @@
* used and the background color not at all.
*/
public void setOverrideTintColor(int color, float overrideAmount) {
+ if (mDark) {
+ color = NO_COLOR;
+ overrideAmount = 0;
+ }
mOverrideTint = color;
mOverrideAmount = overrideAmount;
int newColor = calculateBgColor();
setBackgroundTintColor(newColor);
+ if (!isDimmable() && mNeedsDimming) {
+ mBackgroundNormal.setDrawableAlpha((int) NotificationUtils.interpolate(255,
+ mDimmedAlpha,
+ overrideAmount));
+ } else {
+ mBackgroundNormal.setDrawableAlpha(255);
+ }
}
protected void updateBackgroundTint() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 30ff30f..be221bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -341,14 +341,12 @@
public void appTransitionPending(boolean forced) {
synchronized (mLock) {
- mHandler.removeMessages(MSG_APP_TRANSITION_PENDING);
mHandler.obtainMessage(MSG_APP_TRANSITION_PENDING, forced ? 1 : 0, 0).sendToTarget();
}
}
public void appTransitionCancelled() {
synchronized (mLock) {
- mHandler.removeMessages(MSG_APP_TRANSITION_CANCELLED);
mHandler.sendEmptyMessage(MSG_APP_TRANSITION_CANCELLED);
}
}
@@ -359,7 +357,6 @@
public void appTransitionStarting(long startTime, long duration, boolean forced) {
synchronized (mLock) {
- mHandler.removeMessages(MSG_APP_TRANSITION_STARTING);
mHandler.obtainMessage(MSG_APP_TRANSITION_STARTING, forced ? 1 : 0, 0,
Pair.create(startTime, duration)).sendToTarget();
}
@@ -368,7 +365,6 @@
@Override
public void appTransitionFinished() {
synchronized (mLock) {
- mHandler.removeMessages(MSG_APP_TRANSITION_FINISHED);
mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index dea9e31f..194cdc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -144,4 +144,8 @@
ripple.setColor(ColorStateList.valueOf(color));
}
}
+
+ public void setDrawableAlpha(int drawableAlpha) {
+ mBackground.setAlpha(drawableAlpha);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index f2595e9..78a5194 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -202,7 +202,7 @@
// find the first view that doesn't overlap with the shelf
int notificationIndex = 0;
int notGoneIndex = 0;
- int colorOfViewBeforeLast = 0;
+ int colorOfViewBeforeLast = NO_COLOR;
boolean backgroundForceHidden = false;
if (mHideBackground && !mShelfState.hasItemsInStableShelf) {
backgroundForceHidden = true;
@@ -256,7 +256,10 @@
colorTwoBefore = previousColor;
transitionAmount = inShelfAmount;
}
- if (isLastChild && colorOfViewBeforeLast != NO_COLOR) {
+ if (isLastChild) {
+ if (colorOfViewBeforeLast == NO_COLOR) {
+ colorOfViewBeforeLast = ownColorUntinted;
+ }
row.setOverrideTintColor(colorOfViewBeforeLast, inShelfAmount);
} else {
colorOfViewBeforeLast = ownColorUntinted;
@@ -310,8 +313,9 @@
float fullTransitionAmount;
float iconTransitionAmount;
float shelfStart = getTranslationY();
- if (viewEnd >= shelfStart && (mAmbientState.isShadeExpanded()
- || (!row.isPinned() && !row.isHeadsUpAnimatingAway()))) {
+ if (viewEnd >= shelfStart && (!mAmbientState.isUnlockHintRunning() || row.isInShelf())
+ && (mAmbientState.isShadeExpanded()
+ || (!row.isPinned() && !row.isHeadsUpAnimatingAway()))) {
if (viewStart < shelfStart) {
float fullAmount = (shelfStart - viewStart) / fullHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index 52c053f..f6bd14c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -23,6 +23,7 @@
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.support.annotation.VisibleForTesting;
import android.support.v4.graphics.ColorUtils;
import android.support.v7.graphics.Palette;
import android.util.LayoutDirection;
@@ -57,9 +58,15 @@
private boolean mIsLowPriority;
public MediaNotificationProcessor(Context context, Context packageContext) {
+ this(context, packageContext, new ImageGradientColorizer());
+ }
+
+ @VisibleForTesting
+ MediaNotificationProcessor(Context context, Context packageContext,
+ ImageGradientColorizer colorizer) {
mContext = context;
mPackageContext = packageContext;
- mColorizer = new ImageGradientColorizer();
+ mColorizer = colorizer;
}
/**
@@ -74,6 +81,9 @@
Bitmap bitmap = null;
Drawable drawable = null;
if (largeIcon != null) {
+ // We're transforming the builder, let's make sure all baked in RemoteViews are
+ // rebuilt!
+ builder.setRebuildStyledRemoteViews(true);
drawable = largeIcon.loadDrawable(mPackageContext);
int backgroundColor = 0;
if (notification.isColorizedMedia()) {
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 6a9bfae..0fa8afa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1985,6 +1985,18 @@
}
@Override
+ protected void onUnlockHintFinished() {
+ super.onUnlockHintFinished();
+ mNotificationStackScroller.setUnlockHintRunning(false);
+ }
+
+ @Override
+ protected void onUnlockHintStarted() {
+ super.onUnlockHintStarted();
+ mNotificationStackScroller.setUnlockHintRunning(true);
+ }
+
+ @Override
public KeyguardAffordanceView getLeftIcon() {
return getLayoutDirection() == LAYOUT_DIRECTION_RTL
? mKeyguardBottomArea.getRightView()
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 d342635..e378e871 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -35,7 +35,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.DejankUtils;
-import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.Interpolators;
import com.android.keyguard.LatencyTracker;
@@ -1020,14 +1019,22 @@
@Override
public void run() {
notifyExpandingFinished();
- mStatusBar.onHintFinished();
+ onUnlockHintFinished();
mHintAnimationRunning = false;
}
});
- mStatusBar.onUnlockHintStarted();
+ onUnlockHintStarted();
mHintAnimationRunning = true;
}
+ protected void onUnlockHintFinished() {
+ mStatusBar.onHintFinished();
+ }
+
+ protected void onUnlockHintStarted() {
+ mStatusBar.onUnlockHintStarted();
+ }
+
/**
* Phase 1: Move everything upwards.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index b2b23a55..e409b9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -60,6 +60,7 @@
private boolean mExpansionChanging;
private boolean mPanelFullWidth;
private boolean mPulsing;
+ private boolean mUnlockHintRunning;
public AmbientState(Context context) {
reload(context);
@@ -305,4 +306,12 @@
public void setPanelFullWidth(boolean panelFullWidth) {
mPanelFullWidth = panelFullWidth;
}
+
+ public void setUnlockHintRunning(boolean unlockHintRunning) {
+ mUnlockHintRunning = unlockHintRunning;
+ }
+
+ public boolean isUnlockHintRunning() {
+ return mUnlockHintRunning;
+ }
}
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 2f7a4ed..61fed2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -4230,6 +4230,10 @@
mAmbientState.setPanelFullWidth(isFullWidth);
}
+ public void setUnlockHintRunning(boolean running) {
+ mAmbientState.setUnlockHintRunning(running);
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
new file mode 100644
index 0000000..5d3a86d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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 static org.junit.Assert.assertNotSame;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.app.Notification;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.widget.RemoteViews;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MediaNotificationProcessorTest extends SysuiTestCase {
+
+ private MediaNotificationProcessor mProcessor;
+ private Bitmap mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ private ImageGradientColorizer mColorizer;
+
+ @Before
+ public void setUp() {
+ mColorizer = spy(new TestableColorizer(mBitmap));
+ mProcessor = new MediaNotificationProcessor(getContext(), getContext(), mColorizer);
+ }
+
+ @Test
+ public void testColorizedWithLargeIcon() {
+ Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon(
+ R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setLargeIcon(mBitmap)
+ .setContentText("Text");
+ Notification notification = builder.build();
+ mProcessor.processNotification(notification, builder);
+ verify(mColorizer).colorize(any(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testNotColorizedWithoutLargeIcon() {
+ Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon(
+ R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+ Notification notification = builder.build();
+ mProcessor.processNotification(notification, builder);
+ verifyZeroInteractions(mColorizer);
+ }
+
+ @Test
+ public void testRemoteViewsReset() {
+ Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon(
+ R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setStyle(new Notification.MediaStyle())
+ .setLargeIcon(mBitmap)
+ .setContentText("Text");
+ Notification notification = builder.build();
+ RemoteViews remoteViews = new RemoteViews(getContext().getPackageName(),
+ R.layout.custom_view_dark);
+ notification.contentView = remoteViews;
+ notification.bigContentView = remoteViews;
+ notification.headsUpContentView = remoteViews;
+ mProcessor.processNotification(notification, builder);
+ verify(mColorizer).colorize(any(), anyInt(), anyBoolean());
+ RemoteViews contentView = builder.createContentView();
+ assertNotSame(contentView, remoteViews);
+ contentView = builder.createBigContentView();
+ assertNotSame(contentView, remoteViews);
+ contentView = builder.createHeadsUpContentView();
+ assertNotSame(contentView, remoteViews);
+ }
+
+ public static class TestableColorizer extends ImageGradientColorizer {
+ private final Bitmap mBitmap;
+
+ private TestableColorizer(Bitmap bitmap) {
+ mBitmap = bitmap;
+ }
+
+ @Override
+ public Bitmap colorize(Drawable drawable, int backgroundColor, boolean isRtl) {
+ return mBitmap;
+ }
+ }
+}
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 15381b7..0c5bdea 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
@@ -162,7 +162,7 @@
}
});
block.run();
- countDownLatch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+ countDownLatch.await();
if (exceptionHolder.mException != null) {
throw exceptionHolder.mException;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index 8484bed..eaa073c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -1,18 +1,20 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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
+ * 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.
+ * 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.notification;
+package com.android.systemui.statusbar.notification;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index 76bb6c0..e4c43735 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -1,18 +1,20 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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
+ * 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.
+ * 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.notification;
+package com.android.systemui.statusbar.notification;
import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b2712ff..aad4431 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -4365,7 +4365,7 @@
}
addWidgetLocked(id);
}
- if (id.provider.info != null) {
+ if (id.provider != null && id.provider.info != null) {
stashProviderRestoreUpdateLocked(id.provider,
restoredId, id.appWidgetId);
} else {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index faa6182..35371a7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -621,16 +621,17 @@
@Override
protected Void doInBackground(Void... ignored) {
int numSessionsToRemove;
- ArrayMap<IBinder, Integer> sessionsToRemove;
+
+ SparseArray<IBinder> sessionsToRemove;
synchronized (mLock) {
numSessionsToRemove = mSessions.size();
- sessionsToRemove = new ArrayMap<>(numSessionsToRemove);
+ sessionsToRemove = new SparseArray<>(numSessionsToRemove);
for (int i = 0; i < numSessionsToRemove; i++) {
Session session = mSessions.valueAt(i);
- sessionsToRemove.put(session.getActivityTokenLocked(), session.id);
+ sessionsToRemove.put(session.id, session.getActivityTokenLocked());
}
}
@@ -640,7 +641,7 @@
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) {
+ if (am.getActivityClassForToken(sessionsToRemove.valueAt(i)) != null) {
sessionsToRemove.removeAt(i);
i--;
numSessionsToRemove--;
@@ -652,9 +653,10 @@
synchronized (mLock) {
for (int i = 0; i < numSessionsToRemove; i++) {
- Session sessionToRemove = mSessions.get(sessionsToRemove.valueAt(i));
+ Session sessionToRemove = mSessions.get(sessionsToRemove.keyAt(i));
- if (sessionToRemove != null) {
+ if (sessionToRemove != null && sessionsToRemove.valueAt(i)
+ == sessionToRemove.getActivityTokenLocked()) {
if (sessionToRemove.isSavingLocked()) {
if (sVerbose) {
Slog.v(TAG, "Session " + sessionToRemove.id + " is saving");
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 4746ee9..6d58e72 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1122,7 +1122,7 @@
private void notifyUnavailableToClient() {
synchronized (mLock) {
- if (!mHasCallback) return;
+ if (!mHasCallback || mCurrentViewId == null) return;
try {
mClient.notifyNoFillUi(id, mCurrentViewId);
} catch (RemoteException e) {
@@ -1371,10 +1371,15 @@
pw.print(prefix); pw.print("id: "); pw.println(id);
pw.print(prefix); pw.print("uid: "); pw.println(uid);
pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
- pw.print(prefix); pw.print("mResponses: "); pw.println(mResponses.size());
- for (int i = 0; i < mResponses.size(); i++) {
- pw.print(prefix2); pw.print('#'); pw.print(i); pw.print(' ');
- pw.println(mResponses.valueAt(i));
+ pw.print(prefix); pw.print("mResponses: ");
+ if (mResponses == null) {
+ pw.println("null");
+ } else {
+ pw.println(mResponses.size());
+ for (int i = 0; i < mResponses.size(); i++) {
+ pw.print(prefix2); pw.print('#'); pw.print(i);
+ pw.print(' '); pw.println(mResponses.valueAt(i));
+ }
}
pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId);
pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size());
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 6093241..73f1705 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -110,10 +110,15 @@
private final CompanionDeviceManagerImpl mImpl;
private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
private IDeviceIdleController mIdleController;
- private IFindDeviceCallback mFindDeviceCallback;
private ServiceConnection mServiceConnection;
private IAppOpsService mAppOpsManager;
+ private IFindDeviceCallback mFindDeviceCallback;
+ private AssociationRequest mRequest;
+ private String mCallingPackage;
+
+ private final Object mLock = new Object();
+
public CompanionDeviceManagerService(Context context) {
super(context);
mImpl = new CompanionDeviceManagerImpl();
@@ -156,8 +161,12 @@
}
private void cleanup() {
- mServiceConnection = unbind(mServiceConnection);
- mFindDeviceCallback = unlinkToDeath(mFindDeviceCallback, this, 0);
+ synchronized (mLock) {
+ mServiceConnection = unbind(mServiceConnection);
+ mFindDeviceCallback = unlinkToDeath(mFindDeviceCallback, this, 0);
+ mRequest = null;
+ mCallingPackage = null;
+ }
}
/**
@@ -222,6 +231,17 @@
}
@Override
+ public void stopScan(AssociationRequest request,
+ IFindDeviceCallback callback,
+ String callingPackage) {
+ if (Objects.equals(request, mRequest)
+ && Objects.equals(callback, mFindDeviceCallback)
+ && Objects.equals(callingPackage, mCallingPackage)) {
+ cleanup();
+ }
+ }
+
+ @Override
public List<String> getAssociations(String callingPackage, int userId)
throws RemoteException {
checkCallerIsSystemOr(callingPackage, userId);
@@ -340,7 +360,11 @@
"onServiceConnected(name = " + name + ", service = "
+ service + ")");
}
+
mFindDeviceCallback = findDeviceCallback;
+ mRequest = request;
+ mCallingPackage = callingPackage;
+
try {
mFindDeviceCallback.asBinder().linkToDeath(
CompanionDeviceManagerService.this, 0);
@@ -348,6 +372,7 @@
cleanup();
return;
}
+
try {
ICompanionDeviceDiscoveryService.Stub
.asInterface(service)
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index c785fb9..b65f54e 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -91,6 +91,7 @@
private static final String REASON_SYSTEM_BOOT = "system boot";
private static final String REASON_UNEXPECTED = "unexpected crash";
private static final String REASON_USER_SWITCH = "user switch";
+ private static final String REASON_RESTORE_USER_SETTING = "restore user setting";
private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind
//Maximum msec to wait for service restart
@@ -119,6 +120,10 @@
private static final int MESSAGE_USER_UNLOCKED = 301;
private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
+ private static final int MESSAGE_RESTORE_USER_SETTING = 500;
+
+ private static final int RESTORE_SETTING_TO_ON = 1;
+ private static final int RESTORE_SETTING_TO_OFF = 0;
private static final int MAX_ERROR_RESTART_RETRIES = 6;
@@ -318,6 +323,26 @@
} else {
if (DBG) Slog.e(TAG, "No Bluetooth Adapter address parameter found");
}
+ } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
+ final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
+ if (Settings.Global.BLUETOOTH_ON.equals(name)) {
+ // The Bluetooth On state may be changed during system restore.
+ final String prevValue = intent.getStringExtra(
+ Intent.EXTRA_SETTING_PREVIOUS_VALUE);
+ final String newValue = intent.getStringExtra(
+ Intent.EXTRA_SETTING_NEW_VALUE);
+
+ if (DBG) Slog.d(TAG, "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" +
+ prevValue + ", newValue=" + newValue);
+
+ if ((newValue != null) && (prevValue != null) && !prevValue.equals(newValue)) {
+ Message msg = mHandler.obtainMessage(MESSAGE_RESTORE_USER_SETTING,
+ newValue.equals("0") ?
+ RESTORE_SETTING_TO_OFF :
+ RESTORE_SETTING_TO_ON, 0);
+ mHandler.sendMessage(msg);
+ }
+ }
}
}
};
@@ -350,12 +375,14 @@
registerForBleScanModeChange();
mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
- IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
+ filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
+ filter.addAction(Intent.ACTION_SETTING_RESTORED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(mReceiver, filter);
- filter = new IntentFilter(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- mContext.registerReceiver(mReceiver, filter);
+
loadStoredNameAndAddress();
if (isBluetoothPersistedStateOn()) {
if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
@@ -1421,6 +1448,20 @@
}
break;
+ case MESSAGE_RESTORE_USER_SETTING:
+ try {
+ if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) {
+ if (DBG) Slog.d(TAG, "Restore Bluetooth state to disabled");
+ disable(REASON_RESTORE_USER_SETTING, true);
+ } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) {
+ if (DBG) Slog.d(TAG, "Restore Bluetooth state to enabled");
+ enable(REASON_RESTORE_USER_SETTING);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG,"Unable to change Bluetooth On setting", e);
+ }
+ break;
+
case MESSAGE_REGISTER_ADAPTER:
{
IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e76ffb1..214edd0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13407,8 +13407,12 @@
public void setRenderThread(int tid) {
synchronized (this) {
ProcessRecord proc;
+ int pid = Binder.getCallingPid();
+ if (pid == Process.myPid()) {
+ demoteSystemServerRenderThread(tid);
+ return;
+ }
synchronized (mPidsSelfLocked) {
- int pid = Binder.getCallingPid();
proc = mPidsSelfLocked.get(pid);
if (proc != null && proc.renderThreadTid == 0 && tid > 0) {
// ensure the tid belongs to the process
@@ -13441,6 +13445,16 @@
}
}
+ /**
+ * We only use RenderThread in system_server to store task snapshots to the disk, which should
+ * happen in the background. Thus, demote render thread from system_server to a lower priority.
+ *
+ * @param tid the tid of the RenderThread
+ */
+ private void demoteSystemServerRenderThread(int tid) {
+ setThreadPriority(tid, Process.THREAD_PRIORITY_BACKGROUND);
+ }
+
@Override
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
@@ -14499,6 +14513,7 @@
// concurrently during execution of this method)
synchronized (this) {
sb.append("Process: ").append(processName).append("\n");
+ sb.append("PID: ").append(process.pid).append("\n");
int flags = process.info.flags;
IPackageManager pm = AppGlobals.getPackageManager();
sb.append("Flags: 0x").append(Integer.toHexString(flags)).append("\n");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index f13dad7..4e00f2d 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -283,6 +283,7 @@
boolean visible; // does this activity's window need to be shown?
boolean visibleIgnoringKeyguard; // is this activity visible, ignoring the fact that Keyguard
// might hide this activity?
+ private boolean mLastSetWindowVisibility; // The last window visibility state that was set.
private boolean mDeferHidingClient; // If true we told WM to defer reporting to the client
// process that it is hidden.
boolean sleeping; // have we told the activity to sleep?
@@ -1581,6 +1582,10 @@
}
void setVisibility(boolean visible) {
+ if (mLastSetWindowVisibility == visible) {
+ return;
+ }
+ mLastSetWindowVisibility = visible;
mWindowContainerController.setVisibility(visible, mDeferHidingClient);
mStackSupervisor.mActivityMetricsLogger.notifyVisibilityChanged(this, visible);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index c5d5867..c56e4ea 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1933,7 +1933,7 @@
*
* @return true if {@param r} is visible taken Keyguard state into account, false otherwise
*/
- private boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible,
+ boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible,
boolean isTop) {
final boolean isInPinnedStack = r.getStack().getStackId() == PINNED_STACK_ID;
final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing();
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4049500..8210c07 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1333,7 +1333,13 @@
}
r.startFreezingScreenLocked(app, 0);
- r.setVisibility(true);
+ if (r.getStack().checkKeyguardVisibility(r, true /* shouldBeVisible */, true /* isTop */)) {
+ // We only set the visibility to true if the activity is allowed to be visible based on
+ // keyguard state. This avoids setting this into motion in window manager that is later
+ // cancelled due to later calls to ensure visible activities that set visibility back to
+ // false.
+ r.setVisibility(true);
+ }
// schedule launch ticks to collect information about slow apps.
r.startLaunchTickingLocked();
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 2e6cd3f..57b1ccc 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -807,7 +807,7 @@
ArrayList<JobStatus> result = new ArrayList<JobStatus>();
for (int i = mJobs.size() - 1; i >= 0; i--) {
if (UserHandle.getUserId(mJobs.keyAt(i)) == userId) {
- ArraySet<JobStatus> jobs = mJobs.get(i);
+ ArraySet<JobStatus> jobs = mJobs.valueAt(i);
if (jobs != null) {
result.addAll(jobs);
}
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
index e6edaf1..1bd2085 100644
--- a/services/core/java/com/android/server/notification/BadgeExtractor.java
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -41,9 +41,10 @@
if (DBG) Slog.d(TAG, "missing config");
return null;
}
+ boolean userWantsBadges = mConfig.badgingEnabled(record.sbn.getUser());
boolean appCanShowBadge =
mConfig.canShowBadge(record.sbn.getPackageName(), record.sbn.getUid());
- if (!appCanShowBadge) {
+ if (!userWantsBadges || !appCanShowBadge) {
record.setShowBadge(false);
} else {
record.setShowBadge(mConfig.getNotificationChannel(record.sbn.getPackageName(),
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index da919ec..9cd0dff 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -927,6 +927,8 @@
};
private final class SettingsObserver extends ContentObserver {
+ private final Uri NOTIFICATION_BADGING_URI
+ = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
private final Uri NOTIFICATION_LIGHT_PULSE_URI
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
private final Uri NOTIFICATION_RATE_LIMIT_URI
@@ -938,6 +940,8 @@
void observe() {
ContentResolver resolver = getContext().getContentResolver();
+ resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
+ false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
@@ -963,6 +967,9 @@
mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
}
+ if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
+ mRankingHelper.updateBadgingEnabled();
+ }
}
}
@@ -1820,6 +1827,12 @@
}
@Override
+ public boolean onlyHasDefaultChannel(String pkg, int uid) {
+ enforceSystemOrSystemUI("onlyHasDefaultChannel");
+ return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
+ }
+
+ @Override
public int getDeletedChannelCount(String pkg, int uid) {
enforceSystemOrSystemUI("getDeletedChannelCount");
return mRankingHelper.getDeletedChannelCount(pkg, uid);
@@ -4022,6 +4035,7 @@
private void handleRankingSort(Message msg) {
if (!(msg.obj instanceof Boolean)) return;
+ if (mRankingHelper == null) return;
boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
synchronized (mNotificationLock) {
final int N = mNotificationList.size();
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 4d19b52..36da04d 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -18,6 +18,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.content.pm.ParceledListSlice;
+import android.os.UserHandle;
import java.util.Collection;
@@ -27,6 +28,7 @@
int getImportance(String packageName, int uid);
void setShowBadge(String packageName, int uid, boolean showBadge);
boolean canShowBadge(String packageName, int uid);
+ boolean badgingEnabled(UserHandle userHandle);
Collection<NotificationChannelGroup> getNotificationChannelGroups(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 788f21d..e83d453 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -21,6 +21,8 @@
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.Preconditions;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -33,10 +35,14 @@
import android.metrics.LogMaker;
import android.os.Build;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService.Ranking;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import org.json.JSONArray;
import org.json.JSONException;
@@ -91,6 +97,7 @@
private final Context mContext;
private final RankingHandler mRankingHandler;
private final PackageManager mPm;
+ private SparseBooleanArray mBadgingEnabled;
public RankingHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
NotificationUsageStats usageStats, String[] extractorNames) {
@@ -100,6 +107,8 @@
mPreliminaryComparator = new NotificationComparator(mContext);
+ updateBadgingEnabled();
+
final int N = extractorNames.length;
mSignalExtractors = new NotificationSignalExtractor[N];
for (int i = 0; i < N; i++) {
@@ -303,7 +312,8 @@
private void createDefaultChannelIfNeeded(Record r) throws NameNotFoundException {
if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- // Already exists
+ r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(
+ mContext.getString(R.string.default_notification_channel_label));
return;
}
@@ -769,6 +779,21 @@
return new ParceledListSlice<>(channels);
}
+ /**
+ * True for pre-O apps that only have the default channel, or pre O apps that have no
+ * channels yet. This method will create the default channel for pre-O apps that don't have it.
+ * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
+ * upgrades.
+ */
+ public boolean onlyHasDefaultChannel(String pkg, int uid) {
+ Record r = getOrCreateRecord(pkg, uid);
+ if (r.channels.size() == 1
+ && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+ return true;
+ }
+ return false;
+ }
+
public int getDeletedChannelCount(String pkg, int uid) {
Preconditions.checkNotNull(pkg);
int deletedCount = 0;
@@ -1111,6 +1136,38 @@
channel.getImportance());
}
+ public void updateBadgingEnabled() {
+ if (mBadgingEnabled == null) {
+ mBadgingEnabled = new SparseBooleanArray();
+ }
+ boolean changed = false;
+ // update the cached values
+ for (int index = 0; index < mBadgingEnabled.size(); index++) {
+ int userId = mBadgingEnabled.keyAt(index);
+ final boolean oldValue = mBadgingEnabled.get(userId);
+ final boolean newValue = Secure.getIntForUser(mContext.getContentResolver(),
+ Secure.NOTIFICATION_BADGING,
+ DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
+ mBadgingEnabled.put(userId, newValue);
+ changed |= oldValue != newValue;
+ }
+ if (changed) {
+ mRankingHandler.requestSort(false);
+ }
+ }
+
+ public boolean badgingEnabled(UserHandle userHandle) {
+ int userId = userHandle.getIdentifier();
+ if (mBadgingEnabled.indexOfKey(userId) < 0) {
+ mBadgingEnabled.put(userId,
+ Secure.getIntForUser(mContext.getContentResolver(),
+ Secure.NOTIFICATION_BADGING,
+ DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
+ }
+ return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
+ }
+
+
private static class Record {
static int UNKNOWN_UID = UserHandle.USER_NULL;
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index bb7ffda..6625331 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -724,6 +724,9 @@
grantRuntimePermissionsLPw(smsPackage, PHONE_PERMISSIONS, userId);
grantRuntimePermissionsLPw(smsPackage, CONTACTS_PERMISSIONS, userId);
grantRuntimePermissionsLPw(smsPackage, SMS_PERMISSIONS, userId);
+ grantRuntimePermissionsLPw(smsPackage, STORAGE_PERMISSIONS, userId);
+ grantRuntimePermissionsLPw(smsPackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissionsLPw(smsPackage, CAMERA_PERMISSIONS, userId);
}
}
@@ -737,6 +740,9 @@
grantRuntimePermissionsLPw(smsPackage, PHONE_PERMISSIONS, false, true, userId);
grantRuntimePermissionsLPw(smsPackage, CONTACTS_PERMISSIONS, false, true, userId);
grantRuntimePermissionsLPw(smsPackage, SMS_PERMISSIONS, false, true, userId);
+ grantRuntimePermissionsLPw(smsPackage, STORAGE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissionsLPw(smsPackage, MICROPHONE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissionsLPw(smsPackage, CAMERA_PERMISSIONS, false, true, userId);
}
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
index bbd4048..575e0f9 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
@@ -21,6 +21,7 @@
import android.os.ShellCommand;
import java.io.PrintWriter;
+import java.util.Locale;
class OtaDexoptShellCommand extends ShellCommand {
final IOtaDexopt mInterface;
@@ -93,7 +94,10 @@
private int runOtaProgress() throws RemoteException {
final float progress = mInterface.getProgress();
final PrintWriter pw = getOutPrintWriter();
- pw.format("%.2f", progress);
+ // Note: The float output is parsed by update_engine. It does needs to be non-localized,
+ // as it's always expected to be "0.xy," never "0,xy" or similar. So use the ROOT
+ // Locale for formatting. (b/37760573)
+ pw.format(Locale.ROOT, "%.2f", progress);
return 0;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1129076..8fcfc8f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -29,7 +29,6 @@
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
@@ -46,7 +45,6 @@
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.Signature;
-import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.FileBridge;
@@ -56,7 +54,6 @@
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
-import android.os.ProxyFileDescriptorCallback;
import android.os.RemoteException;
import android.os.RevocableFileDescriptor;
import android.os.UserHandle;
@@ -1168,19 +1165,7 @@
// Send broadcast to default launcher only if it's a new install
final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
if (success && isNewInstall) {
- UserManagerService ums = UserManagerService.getInstance();
- if (ums != null) {
- final UserInfo parent = ums.getProfileParent(userId);
- final int launcherUid = (parent != null) ? parent.id : userId;
- final ComponentName launcherComponent = mPm.getDefaultHomeActivity(launcherUid);
- if (launcherComponent != null) {
- Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
- .putExtra(PackageInstaller.EXTRA_SESSION, generateInfo())
- .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
- .setPackage(launcherComponent.getPackageName());
- mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
- }
- }
+ mPm.sendSessionCommitBroadcast(generateInfo(), userId);
}
mCallback.onSessionFinished(this, success);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e4b5241..0e91d30 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -382,6 +382,8 @@
private static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_VERIFY = false;
private static final boolean DEBUG_FILTERS = false;
+ private static final boolean DEBUG_PERMISSIONS = false;
+ private static final boolean DEBUG_SHARED_LIBRARIES = false;
// Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService
// and PackageDexOptimizer. All these classes have their own flag to allow switching a single
@@ -2783,10 +2785,12 @@
// skip setup wizard; allow it to keep the high priority filter
continue;
}
- Slog.w(TAG, "Protected action; cap priority to 0;"
- + " package: " + filter.activity.info.packageName
- + " activity: " + filter.activity.className
- + " origPrio: " + filter.getPriority());
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Protected action; cap priority to 0;"
+ + " package: " + filter.activity.info.packageName
+ + " activity: " + filter.activity.className
+ + " origPrio: " + filter.getPriority());
+ }
filter.setPriority(0);
}
}
@@ -3615,7 +3619,7 @@
if (matchFactoryOnly) {
final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
if (ps != null) {
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
return null;
}
return generatePackageInfo(ps, flags, userId);
@@ -3630,14 +3634,14 @@
Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
if (p != null) {
if (filterSharedLibPackageLPr((PackageSetting) p.mExtras,
- Binder.getCallingUid(), userId)) {
+ Binder.getCallingUid(), userId, flags)) {
return null;
}
return generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
}
if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
return null;
}
return generatePackageInfo(ps, flags, userId);
@@ -3646,13 +3650,17 @@
return null;
}
-
- private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId) {
- // System/shell/root get to see all static libs
- final int appId = UserHandle.getAppId(uid);
- if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
- || appId == Process.ROOT_UID) {
- return false;
+ private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
+ int flags) {
+ // Callers can access only the libs they depend on, otherwise they need to explicitly
+ // ask for the shared libraries given the caller is allowed to access all static libs.
+ if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
+ // System/shell/root get to see all static libs
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
+ || appId == Process.ROOT_UID) {
+ return false;
+ }
}
// No package means no static lib as it is always on internal storage
@@ -3847,7 +3855,7 @@
if (!sUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
- if (filterSharedLibPackageLPr(ps, uid, userId)) {
+ if (filterSharedLibPackageLPr(ps, uid, userId, flags)) {
return null;
}
if (ps.pkg == null) {
@@ -3888,7 +3896,7 @@
if (p != null) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) return null;
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
return null;
}
// Note: isEnabledLP() does not apply here - always return info
@@ -4349,7 +4357,6 @@
}
final long identity = Binder.clearCallingIdentity();
try {
- // TODO: We will change version code to long, so in the new API it is long
PackageInfo packageInfo = getPackageInfoVersioned(
libInfo.getDeclaringPackage(), flags, userId);
if (packageInfo == null) {
@@ -4492,7 +4499,8 @@
}
PackageSetting ps = mSettings.getPackageLPr(libEntry.apk);
if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(),
- UserHandle.getUserId(Binder.getCallingUid()))) {
+ UserHandle.getUserId(Binder.getCallingUid()),
+ PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) {
if (libs == null) {
libs = new ArraySet<>();
}
@@ -7547,7 +7555,7 @@
if (listUninstalled) {
list = new ArrayList<>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
continue;
}
final PackageInfo pi = generatePackageInfo(ps, flags, userId);
@@ -7559,7 +7567,7 @@
list = new ArrayList<>(mPackages.size());
for (PackageParser.Package p : mPackages.values()) {
if (filterSharedLibPackageLPr((PackageSetting) p.mExtras,
- Binder.getCallingUid(), userId)) {
+ Binder.getCallingUid(), userId, flags)) {
continue;
}
final PackageInfo pi = generatePackageInfo((PackageSetting)
@@ -7664,7 +7672,7 @@
effectiveFlags |= PackageManager.MATCH_ANY_USER;
}
if (ps.pkg != null) {
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
continue;
}
ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags,
@@ -7688,7 +7696,7 @@
for (PackageParser.Package p : mPackages.values()) {
if (p.mExtras != null) {
PackageSetting ps = (PackageSetting) p.mExtras;
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
continue;
}
ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
@@ -9294,8 +9302,8 @@
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
"Package " + packageName + " requires unavailable shared library "
+ libName + "; failing!");
- } else {
- Slog.w(TAG, "Package " + packageName
+ } else if (DEBUG_SHARED_LIBRARIES) {
+ Slog.i(TAG, "Package " + packageName
+ " desires unavailable shared library "
+ libName + "; ignoring!");
}
@@ -10658,13 +10666,13 @@
// Now that permission groups have a special meaning, we ignore permission
// groups for legacy apps to prevent unexpected behavior. In particular,
- // permissions for one app being granted to someone just becase they happen
+ // permissions for one app being granted to someone just because they happen
// to be in a group defined by another app (before this had no implications).
if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
p.group = mPermissionGroups.get(p.info.group);
// Warn for a permission in an unknown group.
- if (p.info.group != null && p.group == null) {
- Slog.w(TAG, "Permission " + p.info.name + " from package "
+ if (DEBUG_PERMISSIONS && p.info.group != null && p.group == null) {
+ Slog.i(TAG, "Permission " + p.info.name + " from package "
+ p.info.packageName + " in an unknown group " + p.info.group);
}
}
@@ -11017,12 +11025,14 @@
if (ps.pkg != null && ps.pkg.applicationInfo != null &&
!TextUtils.equals(adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) {
ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
- Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi
- + " (requirer="
- + (requirer != null ? requirer.pkg : "null")
- + ", scannedPackage="
- + (scannedPackage != null ? scannedPackage : "null")
- + ")");
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi
+ + " (requirer="
+ + (requirer != null ? requirer.pkg : "null")
+ + ", scannedPackage="
+ + (scannedPackage != null ? scannedPackage : "null")
+ + ")");
+ }
try {
mInstaller.rmdex(ps.codePathString,
getDexCodeInstructionSet(getPreferredInstructionSet()));
@@ -11743,8 +11753,10 @@
if (bp == null || bp.packageSetting == null) {
if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
- Slog.w(TAG, "Unknown permission " + name
- + " in package " + pkg.packageName);
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Unknown permission " + name
+ + " in package " + pkg.packageName);
+ }
}
continue;
}
@@ -11752,14 +11764,18 @@
// Limit ephemeral apps to ephemeral allowed permissions.
if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
- Log.i(TAG, "Denying non-ephemeral permission " + bp.name + " for package "
- + pkg.packageName);
+ if (DEBUG_PERMISSIONS) {
+ Log.i(TAG, "Denying non-ephemeral permission " + bp.name + " for package "
+ + pkg.packageName);
+ }
continue;
}
if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
- Log.i(TAG, "Denying runtime-only permission " + bp.name + " for package "
- + pkg.packageName);
+ if (DEBUG_PERMISSIONS) {
+ Log.i(TAG, "Denying runtime-only permission " + bp.name + " for package "
+ + pkg.packageName);
+ }
continue;
}
@@ -11817,8 +11833,8 @@
} break;
}
- if (DEBUG_INSTALL) {
- Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Granting permission " + perm + " to package " + pkg.packageName);
}
if (grant != GRANT_DENIED) {
@@ -11964,9 +11980,11 @@
default: {
if (packageOfInterest == null
|| packageOfInterest.equals(pkg.packageName)) {
- Slog.w(TAG, "Not granting permission " + perm
- + " to package " + pkg.packageName
- + " because it was previously installed without");
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Not granting permission " + perm
+ + " to package " + pkg.packageName
+ + " because it was previously installed without");
+ }
}
} break;
}
@@ -11985,8 +12003,10 @@
} else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {
// Don't print warning for app op permissions, since it is fine for them
// not to be granted, there is a UI for the user to decide.
- if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
- Slog.w(TAG, "Not granting permission " + perm
+ if (DEBUG_PERMISSIONS
+ && (packageOfInterest == null
+ || packageOfInterest.equals(pkg.packageName))) {
+ Slog.i(TAG, "Not granting permission " + perm
+ " to package " + pkg.packageName
+ " (protectionLevel=" + bp.protectionLevel
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
@@ -12355,10 +12375,12 @@
((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
if (!privilegedApp) {
// non-privileged applications can never define a priority >0
- Slog.w(TAG, "Non-privileged app; cap priority to 0;"
- + " package: " + applicationInfo.packageName
- + " activity: " + intent.activity.className
- + " origPrio: " + intent.getPriority());
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Non-privileged app; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
intent.setPriority(0);
return;
}
@@ -12398,10 +12420,12 @@
// setup wizard gets whatever it wants
return;
}
- Slog.w(TAG, "Protected action; cap priority to 0;"
- + " package: " + intent.activity.info.packageName
- + " activity: " + intent.activity.className
- + " origPrio: " + intent.getPriority());
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Protected action; cap priority to 0;"
+ + " package: " + intent.activity.info.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
intent.setPriority(0);
return;
}
@@ -13603,6 +13627,12 @@
int userId) {
final boolean isSystem = isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
sendPackageAddedForNewUsers(packageName, isSystem, pkgSetting.appId, userId);
+
+ // Send a session commit broadcast
+ final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
+ info.installReason = pkgSetting.getInstallReason(userId);
+ info.appPackageName = packageName;
+ sendSessionCommitBroadcast(info, userId);
}
public void sendPackageAddedForNewUsers(String packageName, boolean isSystem, int appId, int... userIds) {
@@ -20118,11 +20148,27 @@
return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
}
+ public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
+ UserManagerService ums = UserManagerService.getInstance();
+ if (ums != null) {
+ final UserInfo parent = ums.getProfileParent(userId);
+ final int launcherUid = (parent != null) ? parent.id : userId;
+ final ComponentName launcherComponent = getDefaultHomeActivity(launcherUid);
+ if (launcherComponent != null) {
+ Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
+ .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+ .setPackage(launcherComponent.getPackageName());
+ mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
+ }
+ }
+ }
+
/**
* Report the 'Home' activity which is currently set as "always use this one". If non is set
* then reports the most likely home activity or null if there are more than one.
*/
- public ComponentName getDefaultHomeActivity(int userId) {
+ private ComponentName getDefaultHomeActivity(int userId) {
List<ResolveInfo> allHomeCandidates = new ArrayList<>();
ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId);
if (cn != null) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6aff600..8e058ad 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5325,7 +5325,7 @@
@Override
public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
WindowState attached, WindowState imeTarget) {
- final boolean visible = !win.isGoneForLayoutLw() && win.getAttrs().alpha > 0f;
+ final boolean visible = win.isVisibleLw() && win.getAttrs().alpha > 0f;
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisible=" + visible);
applyKeyguardPolicyLw(win, imeTarget);
final int fl = PolicyControl.getWindowFlags(win, attrs);
@@ -5960,15 +5960,23 @@
result &= ~ACTION_PASS_TO_USER;
break;
}
- if (telecomManager.isInCall()
- && (result & ACTION_PASS_TO_USER) == 0) {
- // If we are in call but we decided not to pass the key to
- // the application, just pass it to the session service.
- MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
- event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
- break;
- }
}
+ int audioMode = AudioManager.MODE_NORMAL;
+ try {
+ audioMode = getAudioService().getMode();
+ } catch (Exception e) {
+ Log.e(TAG, "Error getting AudioService in interceptKeyBeforeQueueing.", e);
+ }
+ boolean isInCall = (telecomManager != null && telecomManager.isInCall()) ||
+ audioMode == AudioManager.MODE_IN_COMMUNICATION;
+ if (isInCall && (result & ACTION_PASS_TO_USER) == 0) {
+ // If we are in call but we decided not to pass the key to
+ // the application, just pass it to the session service.
+ MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
+ event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
+ break;
+ }
+
}
if (mUseTvRouting || mHandleVolumeKeysInWM) {
// Defer special key handlings to
@@ -6313,7 +6321,7 @@
try {
getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
- } catch (RemoteException e) {
+ } catch (Exception e) {
Log.e(TAG, "Error dispatching volume up in dispatchTvAudioEvent.", e);
}
break;
@@ -6321,7 +6329,7 @@
try {
getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
- } catch (RemoteException e) {
+ } catch (Exception e) {
Log.e(TAG, "Error dispatching volume down in dispatchTvAudioEvent.", e);
}
break;
@@ -6332,7 +6340,7 @@
AudioManager.ADJUST_TOGGLE_MUTE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
}
- } catch (RemoteException e) {
+ } catch (Exception e) {
Log.e(TAG, "Error dispatching mute in dispatchTvAudioEvent.", e);
}
break;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6a18beb..39b902e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -59,6 +59,7 @@
import android.os.Environment;
import android.os.FileObserver;
import android.os.FileUtils;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Process;
@@ -663,13 +664,35 @@
@Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mLock) {
+ Slog.w(TAG, "Wallpaper service gone: " + name);
+ if (!Objects.equals(name, mWallpaper.wallpaperComponent)) {
+ Slog.e(TAG, "Does not match expected wallpaper component "
+ + mWallpaper.wallpaperComponent);
+ }
mService = null;
mEngine = null;
if (mWallpaper.connection == this) {
- // The wallpaper disappeared. If this isn't a system-default one, track
- // crashes and fall back to default if it continues to misbehave.
+ // There is an inherent ordering race between this callback and the
+ // package monitor that receives notice that a package is being updated,
+ // so we cannot quite trust at this moment that we know for sure that
+ // this is not an update. If we think this is a genuine non-update
+ // wallpaper outage, we do our "wait for reset" work as a continuation,
+ // a short time in the future, specifically to allow any pending package
+ // update message on this same looper thread to be processed.
+ if (!mWallpaper.wallpaperUpdating) {
+ mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
+ 1000);
+ }
+ }
+ }
+ }
+
+ private void processDisconnect(final ServiceConnection connection) {
+ synchronized (mLock) {
+ // The wallpaper disappeared. If this isn't a system-default one, track
+ // crashes and fall back to default if it continues to misbehave.
+ if (connection == mWallpaper.connection) {
final ComponentName wpService = mWallpaper.wallpaperComponent;
- Slog.w(TAG, "Wallpaper service gone: " + wpService);
if (!mWallpaper.wallpaperUpdating
&& mWallpaper.userId == mCurrentUserId
&& !Objects.equals(mDefaultWallpaperComponent, wpService)
@@ -682,7 +705,7 @@
// during {@link #MIN_WALLPAPER_CRASH_TIME} millis.
if (mWallpaper.lastDiedTime != 0
&& mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
- > SystemClock.uptimeMillis()) {
+ > SystemClock.uptimeMillis()) {
Slog.w(TAG, "Reverting to built-in wallpaper!");
clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
} else {
@@ -690,18 +713,22 @@
// If we didn't reset it right away, do so after we couldn't connect to
// it for an extended amount of time to avoid having a black wallpaper.
- FgThread.getHandler().removeCallbacks(mResetRunnable);
- FgThread.getHandler().postDelayed(mResetRunnable,
- WALLPAPER_RECONNECT_TIMEOUT_MS);
+ final Handler fgHandler = FgThread.getHandler();
+ fgHandler.removeCallbacks(mResetRunnable);
+ fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
if (DEBUG_LIVE) {
Slog.i(TAG, "Started wallpaper reconnect timeout for " + wpService);
}
}
- final String flattened = name.flattenToString();
+ final String flattened = wpService.flattenToString();
EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
flattened.substring(0, Math.min(flattened.length(),
MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
}
+ } else {
+ if (DEBUG_LIVE) {
+ Slog.i(TAG, "Wallpaper changed during disconnect tracking; ignoring");
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 82416ec..fc4ec28 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -158,19 +158,23 @@
/**
* Reloads all the resources for the current configuration.
*/
- void reloadResources() {
+ private void reloadResources() {
final Resources res = mService.mContext.getResources();
mMinSize = res.getDimensionPixelSize(
com.android.internal.R.dimen.default_minimal_size_pip_resizable_task);
mDefaultAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
- final Size screenEdgeInsetsDp = Size.parseSize(res.getString(
- com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets));
+ final String screenEdgeInsetsDpString = res.getString(
+ com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets);
+ final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
+ ? Size.parseSize(screenEdgeInsetsDpString)
+ : null;
mDefaultStackGravity = res.getInteger(
com.android.internal.R.integer.config_defaultPictureInPictureGravity);
mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics);
- mScreenEdgeInsets = new Point(dpToPx(screenEdgeInsetsDp.getWidth(), mTmpMetrics),
- dpToPx(screenEdgeInsetsDp.getHeight(), mTmpMetrics));
+ mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point()
+ : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), mTmpMetrics),
+ dpToPx(screenEdgeInsetsDp.getHeight(), mTmpMetrics));
mMinAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
mMaxAspectRatio = res.getFloat(
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index b79173c..1bbe1d0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -275,6 +275,15 @@
mPersister.removeObsoleteFiles(persistentTaskIds, runningUserIds);
}
+ /**
+ * Temporarily pauses/unpauses persisting of task snapshots.
+ *
+ * @param paused Whether task snapshot persisting should be paused.
+ */
+ void setPersisterPaused(boolean paused) {
+ mPersister.setPaused(paused);
+ }
+
void dump(PrintWriter pw, String prefix) {
mCache.dump(pw, prefix);
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index e5c7a72..0287070 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -60,6 +60,8 @@
private final ArrayDeque<WriteQueueItem> mWriteQueue = new ArrayDeque<>();
@GuardedBy("mLock")
private boolean mQueueIdling;
+ @GuardedBy("mLock")
+ private boolean mPaused;
private boolean mStarted;
private final Object mLock = new Object();
private final DirectoryResolver mDirectoryResolver;
@@ -127,6 +129,15 @@
}
}
+ void setPaused(boolean paused) {
+ synchronized (mLock) {
+ mPaused = paused;
+ if (!paused) {
+ mLock.notifyAll();
+ }
+ }
+ }
+
@TestApi
void waitForQueueEmpty() {
while (true) {
@@ -142,7 +153,9 @@
@GuardedBy("mLock")
private void sendToQueueLocked(WriteQueueItem item) {
mWriteQueue.offer(item);
- mLock.notifyAll();
+ if (!mPaused) {
+ mLock.notifyAll();
+ }
}
private File getDirectory(int userId) {
@@ -185,7 +198,11 @@
while (true) {
WriteQueueItem next;
synchronized (mLock) {
- next = mWriteQueue.poll();
+ if (mPaused) {
+ next = null;
+ } else {
+ next = mWriteQueue.poll();
+ }
}
if (next != null) {
next.write();
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 067cc09..d64dc0e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -115,8 +115,8 @@
mAnimationTick = () -> {
synchronized (mService.mWindowMap) {
mAnimationTickScheduled = false;
- animateLocked(mCurrentFrameTime);
}
+ animate(mCurrentFrameTime);
};
mAnimationFrameCallback = frameTimeNs -> {
synchronized (mService.mWindowMap) {
@@ -126,8 +126,8 @@
return;
}
mAnimationTickScheduled = true;
- mSfChoreographer.scheduleAtSfVsync(mAnimationTick);
}
+ mSfChoreographer.scheduleAtSfVsync(mAnimationTick);
};
}
@@ -151,135 +151,158 @@
mDisplayContentsAnimators.delete(displayId);
}
- /** Locked on mService.mWindowMap. */
- private void animateLocked(long frameTimeNs) {
- if (!mInitialized) {
- return;
- }
-
- mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
- mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
- boolean wasAnimating = mAnimating;
- setAnimating(false);
- mAppWindowAnimating = false;
- if (DEBUG_WINDOW_TRACE) {
- Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
- }
-
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animateLocked");
- mService.openSurfaceTransaction();
- SurfaceControl.setAnimationTransaction();
+ /**
+ * DO NOT HOLD THE WINDOW MANAGER LOCK WHILE CALLING THIS METHOD. Reason: the method closes
+ * an animation transaction, that might be blocking until the next sf-vsync, so we want to make
+ * sure other threads can make progress if this happens.
+ */
+ private void animate(long frameTimeNs) {
+ boolean transactionOpen = false;
+ boolean wasAnimating = false;
try {
- final AccessibilityController accessibilityController =
- mService.mAccessibilityController;
- final int numDisplays = mDisplayContentsAnimators.size();
- for (int i = 0; i < numDisplays; i++) {
- final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
- dc.stepAppWindowsAnimation(mCurrentTime);
- DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
+ synchronized (mService.mWindowMap) {
+ if (!mInitialized) {
+ return;
+ }
- final ScreenRotationAnimation screenRotationAnimation =
- displayAnimator.mScreenRotationAnimation;
- if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
- if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
- setAnimating(true);
- } else {
- mBulkUpdateParams |= SET_UPDATE_ROTATION;
- screenRotationAnimation.kill();
- displayAnimator.mScreenRotationAnimation = null;
+ mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
+ mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
+ wasAnimating = mAnimating;
+ setAnimating(false);
+ mAppWindowAnimating = false;
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
+ }
- //TODO (multidisplay): Accessibility supported only for the default display.
- if (accessibilityController != null && dc.isDefaultDisplay) {
- // We just finished rotation animation which means we did not announce
- // the rotation and waited for it to end, announce now.
- accessibilityController.onRotationChangedLocked(
- mService.getDefaultDisplayContentLocked());
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animate");
+ mService.openSurfaceTransaction();
+ transactionOpen = true;
+ SurfaceControl.setAnimationTransaction();
+
+ final AccessibilityController accessibilityController =
+ mService.mAccessibilityController;
+ final int numDisplays = mDisplayContentsAnimators.size();
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+ dc.stepAppWindowsAnimation(mCurrentTime);
+ DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
+
+ final ScreenRotationAnimation screenRotationAnimation =
+ displayAnimator.mScreenRotationAnimation;
+ if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
+ if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
+ setAnimating(true);
+ } else {
+ mBulkUpdateParams |= SET_UPDATE_ROTATION;
+ screenRotationAnimation.kill();
+ displayAnimator.mScreenRotationAnimation = null;
+
+ //TODO (multidisplay): Accessibility supported only for the default
+ // display.
+ if (accessibilityController != null && dc.isDefaultDisplay) {
+ // We just finished rotation animation which means we did not
+ // announce the rotation and waited for it to end, announce now.
+ accessibilityController.onRotationChangedLocked(
+ mService.getDefaultDisplayContentLocked());
+ }
}
}
+
+ // Update animations of all applications, including those
+ // associated with exiting/removed apps
+ ++mAnimTransactionSequence;
+ dc.updateWindowsForAnimator(this);
+ dc.updateWallpaperForAnimator(this);
+ dc.prepareWindowSurfaces();
+ }
+
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+
+ dc.checkAppWindowsReadyToShow();
+
+ final ScreenRotationAnimation screenRotationAnimation =
+ mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
+ if (screenRotationAnimation != null) {
+ screenRotationAnimation.updateSurfacesInTransaction();
+ }
+
+ orAnimating(dc.animateDimLayers());
+ orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
+ //TODO (multidisplay): Magnification is supported only for the default display.
+ if (accessibilityController != null && dc.isDefaultDisplay) {
+ accessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
+ }
}
- // Update animations of all applications, including those
- // associated with exiting/removed apps
- ++mAnimTransactionSequence;
- dc.updateWindowsForAnimator(this);
- dc.updateWallpaperForAnimator(this);
- dc.prepareWindowSurfaces();
- }
-
- for (int i = 0; i < numDisplays; i++) {
- final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
-
- dc.checkAppWindowsReadyToShow();
-
- final ScreenRotationAnimation screenRotationAnimation =
- mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
- if (screenRotationAnimation != null) {
- screenRotationAnimation.updateSurfacesInTransaction();
+ if (mService.mDragState != null) {
+ mAnimating |= mService.mDragState.stepAnimationLocked(mCurrentTime);
}
- orAnimating(dc.animateDimLayers());
- orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (accessibilityController != null && dc.isDefaultDisplay) {
- accessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
+ if (mAnimating) {
+ mService.scheduleAnimationLocked();
}
- }
- if (mService.mDragState != null) {
- mAnimating |= mService.mDragState.stepAnimationLocked(mCurrentTime);
- }
-
- if (mAnimating) {
- mService.scheduleAnimationLocked();
- }
-
- if (mService.mWatermark != null) {
- mService.mWatermark.drawIfNeeded();
+ if (mService.mWatermark != null) {
+ mService.mWatermark.drawIfNeeded();
+ }
}
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
- mService.closeSurfaceTransaction();
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animateLocked");
- }
-
- boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
- boolean doRequest = false;
- if (mBulkUpdateParams != 0) {
- doRequest = mService.mRoot.copyAnimToLayoutParams();
- }
-
- if (hasPendingLayoutChanges || doRequest) {
- mWindowPlacerLocked.requestTraversal();
- }
-
- if (mAnimating && !wasAnimating && Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
- Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
- }
-
- if (!mAnimating && wasAnimating) {
- mWindowPlacerLocked.requestTraversal();
- if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ if (transactionOpen) {
+ mService.closeSurfaceTransaction();
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
}
}
- if (mRemoveReplacedWindows) {
- mService.mRoot.removeReplacedWindows();
- mRemoveReplacedWindows = false;
- }
+ synchronized (mService.mWindowMap) {
+ boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
+ boolean doRequest = false;
+ if (mBulkUpdateParams != 0) {
+ doRequest = mService.mRoot.copyAnimToLayoutParams();
+ }
- mService.stopUsingSavedSurfaceLocked();
- mService.destroyPreservedSurfaceLocked();
- mService.mWindowPlacerLocked.destroyPendingSurfaces();
+ if (hasPendingLayoutChanges || doRequest) {
+ mWindowPlacerLocked.requestTraversal();
+ }
- if (DEBUG_WINDOW_TRACE) {
- Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
- + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
- + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
- + Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY)));
+ if (mAnimating && !wasAnimating) {
+
+ // Usually app transitions but quite a load onto the system already (with all the
+ // things happening in app), so pause task snapshot persisting to not increase the
+ // load.
+ mService.mTaskSnapshotController.setPersisterPaused(true);
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ }
+ }
+
+ if (!mAnimating && wasAnimating) {
+ mWindowPlacerLocked.requestTraversal();
+ mService.mTaskSnapshotController.setPersisterPaused(false);
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ }
+ }
+
+ if (mRemoveReplacedWindows) {
+ mService.mRoot.removeReplacedWindows();
+ mRemoveReplacedWindows = false;
+ }
+
+ mService.stopUsingSavedSurfaceLocked();
+ mService.destroyPreservedSurfaceLocked();
+ mService.mWindowPlacerLocked.destroyPendingSurfaces();
+
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
+ + Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY)));
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index a6b95d6..6cb4ddc 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1310,6 +1310,10 @@
}
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
+ if (mSurfaceController == null) {
+ return;
+ }
+
final WindowState w = mWin;
final LayoutParams attrs = mWin.getAttrs();
final Task task = w.getTask();
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 0cf4994..262516d 100644
--- a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
@@ -18,20 +18,19 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
import android.app.ActivityManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
import android.app.NotificationManager;
-import android.content.Context;
import android.os.UserHandle;
+import android.provider.Settings.Secure;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -43,7 +42,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class BadgeExtractorTest {
+public class BadgeExtractorTest extends NotificationTestCase {
@Mock RankingConfig mConfig;
@@ -59,7 +58,11 @@
MockitoAnnotations.initMocks(this);
}
- private NotificationRecord getNotificationRecord(NotificationChannel channel) {
+ private NotificationRecord getNotificationRecord(boolean showBadge, int importanceHigh) {
+ NotificationChannel channel = new NotificationChannel("a", "a", importanceHigh);
+ channel.setShowBadge(showBadge);
+ when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
+
final Builder builder = new Builder(getContext())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -73,10 +76,6 @@
return r;
}
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
//
// Tests
//
@@ -86,13 +85,9 @@
BadgeExtractor extractor = new BadgeExtractor();
extractor.setConfig(mConfig);
+ when(mConfig.badgingEnabled(mUser)).thenReturn(true);
when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
- NotificationChannel channel =
- new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
- when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
- channel.setShowBadge(false);
-
- NotificationRecord r = getNotificationRecord(channel);
+ NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
extractor.process(r);
@@ -104,13 +99,9 @@
BadgeExtractor extractor = new BadgeExtractor();
extractor.setConfig(mConfig);
+ when(mConfig.badgingEnabled(mUser)).thenReturn(true);
when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false);
- NotificationChannel channel =
- new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_HIGH);
- channel.setShowBadge(true);
- when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
-
- NotificationRecord r = getNotificationRecord(channel);
+ NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
extractor.process(r);
@@ -122,13 +113,9 @@
BadgeExtractor extractor = new BadgeExtractor();
extractor.setConfig(mConfig);
+ when(mConfig.badgingEnabled(mUser)).thenReturn(true);
when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
- NotificationChannel channel =
- new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
- channel.setShowBadge(true);
- when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
-
- NotificationRecord r = getNotificationRecord(channel);
+ NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED);
extractor.process(r);
@@ -140,13 +127,23 @@
BadgeExtractor extractor = new BadgeExtractor();
extractor.setConfig(mConfig);
+ when(mConfig.badgingEnabled(mUser)).thenReturn(true);
when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false);
- NotificationChannel channel =
- new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
- channel.setShowBadge(false);
- when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
+ NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
- NotificationRecord r = getNotificationRecord(channel);
+ extractor.process(r);
+
+ assertFalse(r.canShowBadge());
+ }
+
+ @Test
+ public void testAppYesChannelYesUserNo() throws Exception {
+ BadgeExtractor extractor = new BadgeExtractor();
+ extractor.setConfig(mConfig);
+
+ when(mConfig.badgingEnabled(mUser)).thenReturn(false);
+ when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
+ NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
extractor.process(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 d4904f5..39caa3c 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -34,7 +34,6 @@
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationManager;
-import android.content.Context;
import android.app.NotificationChannel;
import android.graphics.Color;
import android.media.AudioAttributes;
@@ -47,7 +46,6 @@
import android.os.VibrationEffect;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -69,7 +67,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class BuzzBeepBlinkTest {
+public class BuzzBeepBlinkTest extends NotificationTestCase {
@Mock AudioManager mAudioManager;
@Mock Vibrator mVibrator;
@@ -328,10 +326,6 @@
eq(CUSTOM_LIGHT_COLOR), anyInt(), eq(CUSTOM_LIGHT_ON), eq(CUSTOM_LIGHT_OFF));
}
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
//
// Tests
//
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 24cb72e..f92bd84 100644
--- a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
@@ -25,7 +25,6 @@
import android.app.NotificationManager;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -38,7 +37,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class GlobalSortKeyComparatorTest {
+public class GlobalSortKeyComparatorTest extends NotificationTestCase {
private final String PKG = "PKG";
private final int UID = 1111111;
@@ -46,24 +45,23 @@
@Test
public void testComparator() throws Exception {
- Notification n = new Notification.Builder(
- InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+ Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.build();
- NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord left = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
"", 1499), getDefaultChannel());
left.setGlobalSortKey("first");
- NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord right = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
"", 1499), getDefaultChannel());
right.setGlobalSortKey("second");
- NotificationRecord last = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord last = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
@@ -86,16 +84,15 @@
@Test
public void testNoCrash_leftNull() throws Exception {
- Notification n = new Notification.Builder(
- InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+ Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.build();
- NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord left = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
"", 1499), getDefaultChannel());
- NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord right = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
@@ -117,17 +114,16 @@
@Test
public void testNoCrash_rightNull() throws Exception {
- Notification n = new Notification.Builder(
- InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+ Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.build();
- NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord left = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
"", 1499), getDefaultChannel());
left.setGlobalSortKey("not null");
- NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord right = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
diff --git a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
index 05c33a4..8dd1779 100644
--- a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
@@ -34,10 +34,8 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.Context;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -46,15 +44,11 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class GroupHelperTest {
+public class GroupHelperTest extends NotificationTestCase {
private @Mock GroupHelper.Callback mCallback;
private GroupHelper mGroupHelper;
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
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 3dbd803..d325e10 100644
--- a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -24,10 +24,8 @@
import android.app.Notification.Builder;
import android.app.NotificationManager;
import android.app.NotificationChannel;
-import android.content.Context;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -43,7 +41,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ImportanceExtractorTest {
+public class ImportanceExtractorTest extends NotificationTestCase {
@Mock RankingConfig mConfig;
@@ -75,10 +73,6 @@
return r;
}
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
//
// Tests
//
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 dde08fc..1e5f96f 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
@@ -35,7 +35,6 @@
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -51,7 +50,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class NotificationComparatorTest {
+public class NotificationComparatorTest extends NotificationTestCase {
@Mock Context mContext;
@Mock TelecomManager mTm;
@Mock RankingHandler handler;
@@ -83,10 +82,8 @@
MockitoAnnotations.initMocks(this);
int userId = UserHandle.myUserId();
- when(mContext.getResources()).thenReturn(
- InstrumentationRegistry.getTargetContext().getResources());
- when(mContext.getContentResolver()).thenReturn(
- InstrumentationRegistry.getTargetContext().getContentResolver());
+ when(mContext.getResources()).thenReturn(getContext().getResources());
+ when(mContext.getContentResolver()).thenReturn(getContext().getContentResolver());
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getSystemService(eq(Context.TELECOM_SERVICE))).thenReturn(mTm);
when(mTm.getDefaultDialerPackage()).thenReturn(callPkg);
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
index f0f4c4d..725e8f2 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -38,7 +38,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class NotificationListenerServiceTest {
+public class NotificationListenerServiceTest extends NotificationTestCase {
private String[] mKeys = new String[] { "key", "key1", "key2", "key3"};
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 177c02d..9afb2d2 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -52,9 +52,9 @@
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
+import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -75,7 +75,7 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
-public class NotificationManagerServiceTest {
+public class NotificationManagerServiceTest extends NotificationTestCase {
private static final long WAIT_FOR_IDLE_TIMEOUT = 2;
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
private final int uid = Binder.getCallingUid();
@@ -86,7 +86,7 @@
private IPackageManager mPackageManager;
@Mock
private PackageManager mPackageManagerClient;
- private Context mContext = InstrumentationRegistry.getTargetContext();
+ private Context mContext = getContext();
private final String PKG = mContext.getPackageName();
private TestableLooper mTestableLooper;
@Mock
@@ -122,6 +122,12 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+
+ // most tests assume badging is enabled
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 1,
+ UserHandle.getUserHandleForUid(uid).getIdentifier());
+
mNotificationManagerService = new TestableNotificationManagerService(mContext);
// MockPackageManager - default returns ApplicationInfo with matching calling UID
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 1c8ca84..267d2a6 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
@@ -40,7 +40,6 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -56,7 +55,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class NotificationRecordTest {
+public class NotificationRecordTest extends NotificationTestCase {
private final Context mMockContext = Mockito.mock(Context.class);
@Mock PackageManager mPm;
@@ -96,8 +95,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mMockContext.getResources()).thenReturn(
- InstrumentationRegistry.getContext().getResources());
+ when(mMockContext.getResources()).thenReturn(getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java b/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java
new file mode 100644
index 0000000..cc30aab
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java
@@ -0,0 +1,33 @@
+/*
+ * 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.server.notification;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.testing.TestableContext;
+
+import org.junit.Rule;
+
+
+public class NotificationTestCase {
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(InstrumentationRegistry.getContext(), null);
+
+ protected Context getContext() {
+ return mContext;
+ }
+}
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 30d6812..0f8c815 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -49,11 +49,14 @@
import android.net.Uri;
import android.os.Build;
import android.os.UserHandle;
+import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContext;
+import android.testing.TestableSettingsProvider;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.Xml;
@@ -81,11 +84,13 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class RankingHelperTest {
+public class RankingHelperTest extends NotificationTestCase {
private static final String PKG = "com.android.server.notification";
private static final int UID = 0;
+ private static final UserHandle USER = UserHandle.getUserHandleForUid(UID);
private static final String UPDATED_PKG = "updatedPkg";
private static final int UID2 = 1111111;
+ private static final UserHandle USER2 = UserHandle.getUserHandleForUid(UID2);
private static final String TEST_CHANNEL_ID = "test_channel_id";
@Mock NotificationUsageStats mUsageStats;
@@ -106,10 +111,6 @@
private RankingHelper mHelper;
private AudioAttributes mAudioAttributes;
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -126,6 +127,9 @@
InstrumentationRegistry.getContext().getResources());
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getApplicationInfo()).thenReturn(legacy);
+ // most tests assume badging is enabled
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID));
mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats,
new String[] {ImportanceExtractor.class.getName()});
@@ -869,6 +873,15 @@
}
@Test
+ public void testOnlyHasDefaultChannel() throws Exception {
+ assertTrue(mHelper.onlyHasDefaultChannel(PKG, UID));
+ assertFalse(mHelper.onlyHasDefaultChannel(UPDATED_PKG, UID2));
+
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG, UID));
+ }
+
+ @Test
public void testCreateChannel_defaultChannelId() throws Exception {
try {
mHelper.createNotificationChannel(PKG, UID, new NotificationChannel(
@@ -1199,4 +1212,36 @@
object.getInt("channelCount"));
}
}
+
+ @Test
+ public void testBadgingOverrideTrue() throws Exception {
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 1,
+ USER.getIdentifier());
+ mHelper.updateBadgingEnabled(); // would be called by settings observer
+ assertTrue(mHelper.badgingEnabled(USER));
+ }
+
+ @Test
+ public void testBadgingOverrideFalse() throws Exception {
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 0,
+ USER.getIdentifier());
+ mHelper.updateBadgingEnabled(); // would be called by settings observer
+ assertFalse(mHelper.badgingEnabled(USER));
+ }
+
+ @Test
+ public void testBadgingOverrideUserIsolation() throws Exception {
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 0,
+ USER.getIdentifier());
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 1,
+ USER2.getIdentifier());
+ mHelper.updateBadgingEnabled(); // would be called by settings observer
+ assertFalse(mHelper.badgingEnabled(USER));
+ assertTrue(mHelper.badgingEnabled(USER2));
+ }
+
}
diff --git a/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
index 07f3162..e354267 100644
--- a/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
@@ -26,7 +26,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class RateEstimatorTest {
+public class RateEstimatorTest extends NotificationTestCase {
private long mTestStartTime;
private RateEstimator mEstimator;
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 bc25860..07b21fb 100644
--- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
@@ -27,11 +27,9 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.Context;
import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Slog;
@@ -51,7 +49,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SnoozeHelperTest {
+public class SnoozeHelperTest extends NotificationTestCase {
private static final String TEST_CHANNEL_ID = "test_channel_id";
@Mock SnoozeHelper.Callback mCallback;
@@ -60,10 +58,6 @@
private SnoozeHelper mSnoozeHelper;
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java
index d09b858..4ac0c65 100644
--- a/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -32,7 +32,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ValidateNotificationPeopleTest {
+public class ValidateNotificationPeopleTest extends NotificationTestCase {
@Test
public void testNoExtra() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 0167654..32eee84 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -120,18 +120,21 @@
@After
public void tearDown() throws Exception {
final LinkedList<WindowState> nonCommonWindows = new LinkedList();
- sWm.mRoot.forAllWindows(w -> {
- if (!mCommonWindows.contains(w)) {
- nonCommonWindows.addLast(w);
+
+ synchronized (sWm.mWindowMap) {
+ sWm.mRoot.forAllWindows(w -> {
+ if (!mCommonWindows.contains(w)) {
+ nonCommonWindows.addLast(w);
+ }
+ }, true /* traverseTopToBottom */);
+
+ while (!nonCommonWindows.isEmpty()) {
+ nonCommonWindows.pollLast().removeImmediately();
}
- }, true /* traverseTopToBottom */);
- while (!nonCommonWindows.isEmpty()) {
- nonCommonWindows.pollLast().removeImmediately();
+ mDisplayContent.removeImmediately();
+ sWm.mInputMethodTarget = null;
}
-
- mDisplayContent.removeImmediately();
- sWm.mInputMethodTarget = null;
}
private WindowState createCommonWindow(WindowState parent, int type, String name) {
diff --git a/services/usb/Android.mk b/services/usb/Android.mk
index f6d212b..57bf57d 100644
--- a/services/usb/Android.mk
+++ b/services/usb/Android.mk
@@ -7,8 +7,9 @@
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
-LOCAL_JAVA_LIBRARIES := services.core
-LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.usb-V1.0-java-static \
-android.hidl.manager-V1.0-java-static
+LOCAL_JAVA_LIBRARIES := services.core \
+android.hidl.manager-V1.0-java
+
+LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.usb-V1.0-java
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index eddcbda..0a67669 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -855,7 +855,7 @@
if (!mConnected) {
// restore defaults when USB is disconnected
Slog.i(TAG, "Disconnect, setting usb functions to null");
- setEnabledFunctions(null, true, false);
+ setEnabledFunctions(null, !mAdbEnabled, false);
}
updateUsbFunctions();
} else {
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 5399bb9..840ae22 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -841,7 +841,7 @@
// Only one of device and accessory should be non-null.
private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
UsbDevice device, UsbAccessory accessory) {
- if (info.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
+ if (isForwardMatch(info)) {
return true;
}
@@ -902,6 +902,17 @@
}
/**
+ * If this match used to forward the intent to another profile?
+ *
+ * @param match The match
+ *
+ * @return {@code true} iff this is such a forward match
+ */
+ private boolean isForwardMatch(@NonNull ResolveInfo match) {
+ return match.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE);
+ }
+
+ /**
* Only return those matches with the highest priority.
*
* @param matches All matches, some might have lower priority
@@ -909,16 +920,23 @@
* @return The matches with the highest priority
*/
@NonNull
- private ArrayList<ResolveInfo> preferHighPriority(
- @NonNull ArrayList<ResolveInfo> matches) {
+ private ArrayList<ResolveInfo> preferHighPriority(@NonNull ArrayList<ResolveInfo> matches) {
SparseArray<ArrayList<ResolveInfo>> highestPriorityMatchesByUserId = new SparseArray<>();
SparseIntArray highestPriorityByUserId = new SparseIntArray();
+ ArrayList<ResolveInfo> forwardMatches = new ArrayList<>();
// Create list of highest priority matches per user in highestPriorityMatchesByUserId
int numMatches = matches.size();
for (int matchNum = 0; matchNum < numMatches; matchNum++) {
ResolveInfo match = matches.get(matchNum);
+ // Unnecessary forward matches are filtered out later, hence collect them all to add
+ // them below
+ if (isForwardMatch(match)) {
+ forwardMatches.add(match);
+ continue;
+ }
+
// If this a previously unknown user?
if (highestPriorityByUserId.indexOfKey(match.targetUserId) < 0) {
highestPriorityByUserId.put(match.targetUserId, Integer.MIN_VALUE);
@@ -940,9 +958,10 @@
}
}
- // Combine all users back together. This means that all matches have the same priority for a
- // user. Matches for different users might have different priority.
- ArrayList<ResolveInfo> combinedMatches = new ArrayList<>();
+ // Combine all users (+ forward matches) back together. This means that all non-forward
+ // matches have the same priority for a user. Matches for different users might have
+ // different priority.
+ ArrayList<ResolveInfo> combinedMatches = new ArrayList<>(forwardMatches);
int numMatchArrays = highestPriorityMatchesByUserId.size();
for (int matchArrayNum = 0; matchArrayNum < numMatchArrays; matchArrayNum++) {
combinedMatches.addAll(highestPriorityMatchesByUserId.valueAt(matchArrayNum));
@@ -951,6 +970,51 @@
return combinedMatches;
}
+ /**
+ * If there are no matches for a profile, remove the forward intent to this profile.
+ *
+ * @param rawMatches The matches that contain all forward intents
+ *
+ * @return The matches with the unnecessary forward intents removed
+ */
+ @NonNull private ArrayList<ResolveInfo> removeForwardIntentIfNotNeeded(
+ @NonNull ArrayList<ResolveInfo> rawMatches) {
+ final int numRawMatches = rawMatches.size();
+
+ // The raw matches contain the activities that can be started but also the intents to
+ // forward the intent to the other profile
+ int numParentActivityMatches = 0;
+ int numNonParentActivityMatches = 0;
+ for (int i = 0; i < numRawMatches; i++) {
+ final ResolveInfo rawMatch = rawMatches.get(i);
+ if (!isForwardMatch(rawMatch)) {
+ if (UserHandle.getUserHandleForUid(
+ rawMatch.activityInfo.applicationInfo.uid).equals(mParentUser)) {
+ numParentActivityMatches++;
+ } else {
+ numNonParentActivityMatches++;
+ }
+ }
+ }
+
+ // If only one profile has activity matches, we need to remove all switch intents
+ if (numParentActivityMatches == 0 || numNonParentActivityMatches == 0) {
+ ArrayList<ResolveInfo> matches = new ArrayList<>(
+ numParentActivityMatches + numNonParentActivityMatches);
+
+ for (int i = 0; i < numRawMatches; i++) {
+ ResolveInfo rawMatch = rawMatches.get(i);
+ if (!isForwardMatch(rawMatch)) {
+ matches.add(rawMatch);
+ }
+ }
+ return matches;
+
+ } else {
+ return rawMatches;
+ }
+ }
+
private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent);
@@ -961,7 +1025,8 @@
matches.add(resolveInfo);
}
}
- return preferHighPriority(matches);
+
+ return removeForwardIntentIfNotNeeded(preferHighPriority(matches));
}
private final ArrayList<ResolveInfo> getAccessoryMatchesLocked(
@@ -975,7 +1040,8 @@
matches.add(resolveInfo);
}
}
- return preferHighPriority(matches);
+
+ return removeForwardIntentIfNotNeeded(preferHighPriority(matches));
}
public void deviceAttached(UsbDevice device) {
@@ -1067,34 +1133,16 @@
* Start the appropriate package when an device/accessory got attached.
*
* @param intent The intent to start the package
- * @param rawMatches The available resolutions of the intent
+ * @param matches The available resolutions of the intent
* @param defaultActivity The default activity for the device (if set)
* @param device The device if a device was attached
* @param accessory The accessory if a device was attached
*/
- private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> rawMatches,
+ private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches,
@Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device,
@Nullable UsbAccessory accessory) {
- final int numRawMatches = rawMatches.size();
-
- // The raw matches contain the activities that can be started but also the intents to switch
- // between the profiles
- int numParentActivityMatches = 0;
- int numNonParentActivityMatches = 0;
- for (int i = 0; i < numRawMatches; i++) {
- final ResolveInfo rawMatch = rawMatches.get(i);
- if (!rawMatch.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
- if (UserHandle.getUserHandleForUid(
- rawMatch.activityInfo.applicationInfo.uid).equals(mParentUser)) {
- numParentActivityMatches++;
- } else {
- numNonParentActivityMatches++;
- }
- }
- }
-
// don't show the resolver activity if there are no choices available
- if (numParentActivityMatches + numNonParentActivityMatches == 0) {
+ if (matches.size() == 0) {
if (accessory != null) {
String uri = accessory.getUri();
if (uri != null && uri.length() > 0) {
@@ -1117,21 +1165,6 @@
return;
}
- // If only one profile has activity matches, we need to remove all switch intents
- ArrayList<ResolveInfo> matches;
- if (numParentActivityMatches == 0 || numNonParentActivityMatches == 0) {
- matches = new ArrayList<>(numParentActivityMatches + numNonParentActivityMatches);
-
- for (int i = 0; i < numRawMatches; i++) {
- ResolveInfo rawMatch = rawMatches.get(i);
- if (!rawMatch.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
- matches.add(rawMatch);
- }
- }
- } else {
- matches = rawMatches;
- }
-
if (defaultActivity != null) {
UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser(
UserHandle.getUserId(defaultActivity.applicationInfo.uid));
@@ -1216,10 +1249,10 @@
if (matches.size() == 1) {
final ActivityInfo activityInfo = matches.get(0).activityInfo;
if (activityInfo != null) {
- // bypass dialog and launch the only matching activity
if (mDisablePermissionDialogs) {
return activityInfo;
}
+ // System apps are considered default unless there are other matches
if (activityInfo.applicationInfo != null
&& (activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
!= 0) {
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 2d3d79a..bcde519 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -986,6 +986,15 @@
* @hide
*/
@Override
+ public int installExistingPackage(String packageName, int installReason)
+ throws NameNotFoundException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException {
throw new UnsupportedOperationException();
diff --git a/tests/testables/src/android/testing/TestableSettingsProvider.java b/tests/testables/src/android/testing/TestableSettingsProvider.java
index 13056cf..fe97bca1 100644
--- a/tests/testables/src/android/testing/TestableSettingsProvider.java
+++ b/tests/testables/src/android/testing/TestableSettingsProvider.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.Settings;
import android.test.mock.MockContentProvider;
import android.util.Log;
@@ -48,9 +49,10 @@
}
void clearValuesAndCheck(Context context) {
- mValues.put(key("global", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
- mValues.put(key("secure", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
- mValues.put(key("system", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
+ int userId = UserHandle.myUserId();
+ mValues.put(key("global", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
+ mValues.put(key("secure", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
+ mValues.put(key("system", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
// Verify that if any test is using TestableContext, they all have the correct settings
// provider.
@@ -66,11 +68,12 @@
public Bundle call(String method, String arg, Bundle extras) {
// Methods are "GET_system", "GET_global", "PUT_secure", etc.
+ final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, 0);
final String[] commands = method.split("_", 2);
final String op = commands[0];
final String table = commands[1];
- String k = key(table, arg);
+ String k = key(table, arg, userId);
String value;
Bundle out = new Bundle();
switch (op) {
@@ -103,8 +106,13 @@
return out;
}
- private static String key(String table, String key) {
- return table + "_" + key;
+ private static String key(String table, String key, int userId) {
+ if ("global".equals(table)) {
+ return table + "_" + key;
+ } else {
+ return table + "_" + userId + "_" + key;
+ }
+
}
/**
diff --git a/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java b/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
index 1f71867..0e2cc57 100644
--- a/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
+++ b/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
@@ -66,6 +66,16 @@
}
@Test
+ public void testSeparateUsers() {
+ Secure.putStringForUser(mContentResolver, NONEXISTENT_SETTING, "something", 0);
+ Secure.putStringForUser(mContentResolver, NONEXISTENT_SETTING, "else", 1);
+ assertEquals("something",
+ Secure.getStringForUser(mContentResolver, NONEXISTENT_SETTING, 0));
+ assertEquals("else",
+ Secure.getStringForUser(mContentResolver, NONEXISTENT_SETTING, 1));
+ }
+
+ @Test
public void testPassThrough() {
// Grab the value of a setting that is not overridden.
assertTrue(Secure.getInt(mContentResolver, Secure.USER_SETUP_COMPLETE, 0) != 0);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 764eeeb..1b5a54d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -621,6 +621,12 @@
}
@Override
+ public int installExistingPackage(String packageName, int installReason)
+ throws NameNotFoundException {
+ return 0;
+ }
+
+ @Override
public int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException {
return 0;
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf
deleted file mode 100644
index 2852302..0000000
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf
deleted file mode 100644
index b7bf5b4..0000000
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 2b5e0f9..7b565b1 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -389,7 +389,8 @@
assertTrue(sRenderMessages.isEmpty());
}
- @Test
+ //@Test
+ // Temporarily disabled (b/37725933)
public void testFonts() throws ClassNotFoundException {
// TODO: styles seem to be broken in TextView
renderAndVerify("fonts_test.xml", "font_test.png");