Merge "Do not hold WM lock while closing animation transaction" 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/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/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 ba41933..3cd54b8 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9955,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/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/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/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 6b18591..be4df51 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;
/*
@@ -957,6 +963,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;
@@ -5483,6 +5614,9 @@
LongSamplingCounter mSystemCpuTime;
LongSamplingCounter[][] mCpuClusterSpeed;
+ LongSamplingCounterArray mCpuFreqTimeMs;
+ LongSamplingCounterArray mScreenOffCpuFreqTimeMs;
+
/**
* The statistics we have collected for this uid's wake locks.
*/
@@ -5560,6 +5694,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();
}
@@ -6354,6 +6524,13 @@
}
}
+ if (mCpuFreqTimeMs != null) {
+ mCpuFreqTimeMs.reset(false);
+ }
+ if (mScreenOffCpuFreqTimeMs != null) {
+ mScreenOffCpuFreqTimeMs.reset(false);
+ }
+
resetLongCounterIfNotNull(mMobileRadioApWakeupCount, false);
resetLongCounterIfNotNull(mWifiRadioApWakeupCount, false);
@@ -6523,6 +6700,13 @@
}
}
+ if (mCpuFreqTimeMs != null) {
+ mCpuFreqTimeMs.detach();
+ }
+ if (mScreenOffCpuFreqTimeMs != null) {
+ mScreenOffCpuFreqTimeMs.detach();
+ }
+
detachLongCounterIfNotNull(mMobileRadioApWakeupCount);
detachLongCounterIfNotNull(mWifiRadioApWakeupCount);
}
@@ -6739,6 +6923,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);
@@ -6987,6 +7184,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;
@@ -8192,6 +8401,10 @@
}
}
+ public long[] getCpuFreqs() {
+ return mCpuFreqs;
+ }
+
public BatteryStatsImpl(File systemDir, Handler handler, ExternalStatsSync externalSync) {
this(new SystemClocks(), systemDir, handler, externalSync, null);
}
@@ -9812,6 +10025,8 @@
}
});
+ readKernelUidCpuFreqTimesLocked();
+
final long elapse = (mClocks.elapsedRealtime() - startTimeMs);
if (DEBUG_ENERGY_CPU || (elapse >= 100)) {
Slog.d(TAG, "Reading cpu stats took " + elapse + " ms");
@@ -9900,6 +10115,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;
@@ -10988,6 +11227,8 @@
}
}
+ mCpuFreqs = in.createLongArray();
+
final int NU = in.readInt();
if (NU > 10000) {
throw new ParcelFormatException("File corrupt: too many uids " + NU);
@@ -11111,6 +11352,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 {
@@ -11360,6 +11615,8 @@
}
}
+ out.writeLongArray(mCpuFreqs);
+
final int NU = mUidStats.size();
out.writeInt(NU);
for (int iu = 0; iu < NU; iu++) {
@@ -11504,6 +11761,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);
@@ -11794,6 +12064,8 @@
mFlashlightTurnedOnTimers.clear();
mCameraTurnedOnTimers.clear();
+ mCpuFreqs = in.createLongArray();
+
int numUids = in.readInt();
mUidStats.clear();
for (int i = 0; i < numUids; i++) {
@@ -11953,6 +12225,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/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/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/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/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/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/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/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/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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6cf54c8..9cd0dff 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1827,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);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 2c51510..e83d453 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -312,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;
}
@@ -778,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;
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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 093cdbc..3c42983 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);
}
}
@@ -9298,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!");
}
@@ -10662,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);
}
}
@@ -11021,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()));
@@ -11747,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;
}
@@ -11756,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;
}
@@ -11821,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) {
@@ -11968,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;
}
@@ -11989,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)
@@ -12359,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;
}
@@ -12402,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;
}
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 b7f5186..0f8c815 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -873,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(
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) {