Merge "Refactoring FBE APIs based on council feedback." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 2d2df74..889ed59 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6265,6 +6265,8 @@
method public long getIntervalMillis();
method public long getMaxExecutionDelayMillis();
method public long getMinLatencyMillis();
+ method public static final long getMinimumFlex();
+ method public static final long getMinimumPeriod();
method public int getNetworkType();
method public android.content.ComponentName getService();
method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
@@ -6278,8 +6280,6 @@
field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
- field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
- field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -19260,7 +19260,6 @@
method public boolean hasCarrierPhase();
method public boolean hasCarrierPhaseUncertainty();
method public boolean hasSnrInDb();
- method public boolean isPseudorangeRateCorrected();
method public void writeToParcel(android.os.Parcel, int);
field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4
field public static final int ADR_STATE_RESET = 2; // 0x2
@@ -57450,6 +57449,10 @@
method public static void parallelSort(T[], int, int);
method public static void parallelSort(T[], java.util.Comparator<? super T>);
method public static void parallelSort(T[], int, int, java.util.Comparator<? super T>);
+ method public static void setAll(T[], java.util.function.IntFunction<? extends T>);
+ method public static void setAll(int[], java.util.function.IntUnaryOperator);
+ method public static void setAll(long[], java.util.function.IntToLongFunction);
+ method public static void setAll(double[], java.util.function.IntToDoubleFunction);
method public static void sort(int[]);
method public static void sort(int[], int, int);
method public static void sort(long[]);
diff --git a/api/system-current.txt b/api/system-current.txt
index 6764eda..895e376 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6531,6 +6531,8 @@
method public long getIntervalMillis();
method public long getMaxExecutionDelayMillis();
method public long getMinLatencyMillis();
+ method public static final long getMinimumFlex();
+ method public static final long getMinimumPeriod();
method public int getNetworkType();
method public android.content.ComponentName getService();
method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
@@ -6544,8 +6546,6 @@
field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
- field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
- field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -9929,6 +9929,7 @@
method public void setAppIcon(android.graphics.Bitmap);
method public void setAppLabel(java.lang.CharSequence);
method public void setAppPackageName(java.lang.String);
+ method public void setDontKillApp(boolean);
method public void setGrantedRuntimePermissions(java.lang.String[]);
method public void setInstallLocation(int);
method public void setOriginatingUid(int);
@@ -20431,7 +20432,6 @@
method public boolean hasCarrierPhase();
method public boolean hasCarrierPhaseUncertainty();
method public boolean hasSnrInDb();
- method public boolean isPseudorangeRateCorrected();
method public void writeToParcel(android.os.Parcel, int);
field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4
field public static final int ADR_STATE_RESET = 2; // 0x2
@@ -25719,9 +25719,11 @@
public class ScoredNetwork implements android.os.Parcelable {
ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve);
+ ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.ScoredNetwork> CREATOR;
+ field public final boolean meteredHint;
field public final android.net.NetworkKey networkKey;
field public final android.net.RssiCurve rssiCurve;
}
@@ -48846,7 +48848,9 @@
public static abstract interface WebViewProvider.ViewDelegate {
method public abstract boolean dispatchKeyEvent(android.view.KeyEvent);
+ method public abstract android.view.View findFocus(android.view.View);
method public abstract android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
+ method public abstract android.os.Handler getHandler(android.os.Handler);
method public abstract void onActivityResult(int, int, android.content.Intent);
method public abstract void onAttachedToWindow();
method public abstract void onConfigurationChanged(android.content.res.Configuration);
@@ -60544,6 +60548,10 @@
method public static void parallelSort(T[], int, int);
method public static void parallelSort(T[], java.util.Comparator<? super T>);
method public static void parallelSort(T[], int, int, java.util.Comparator<? super T>);
+ method public static void setAll(T[], java.util.function.IntFunction<? extends T>);
+ method public static void setAll(int[], java.util.function.IntUnaryOperator);
+ method public static void setAll(long[], java.util.function.IntToLongFunction);
+ method public static void setAll(double[], java.util.function.IntToDoubleFunction);
method public static void sort(int[]);
method public static void sort(int[], int, int);
method public static void sort(long[]);
diff --git a/api/test-current.txt b/api/test-current.txt
index 981cb69..908a50e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6269,6 +6269,8 @@
method public long getIntervalMillis();
method public long getMaxExecutionDelayMillis();
method public long getMinLatencyMillis();
+ method public static final long getMinimumFlex();
+ method public static final long getMinimumPeriod();
method public int getNetworkType();
method public android.content.ComponentName getService();
method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
@@ -6282,8 +6284,6 @@
field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
- field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
- field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -19290,7 +19290,6 @@
method public boolean hasCarrierPhase();
method public boolean hasCarrierPhaseUncertainty();
method public boolean hasSnrInDb();
- method public boolean isPseudorangeRateCorrected();
method public void reset();
method public void resetCarrierCycles();
method public void resetCarrierFrequencyHz();
@@ -19308,7 +19307,6 @@
method public void setCn0DbHz(double);
method public void setConstellationType(int);
method public void setMultipathIndicator(int);
- method public void setPseudorangeRateCorrected(boolean);
method public void setPseudorangeRateMetersPerSecond(double);
method public void setPseudorangeRateUncertaintyMetersPerSecond(double);
method public void setReceivedSvTimeNanos(long);
@@ -57524,6 +57522,10 @@
method public static void parallelSort(T[], int, int);
method public static void parallelSort(T[], java.util.Comparator<? super T>);
method public static void parallelSort(T[], int, int, java.util.Comparator<? super T>);
+ method public static void setAll(T[], java.util.function.IntFunction<? extends T>);
+ method public static void setAll(int[], java.util.function.IntUnaryOperator);
+ method public static void setAll(long[], java.util.function.IntToLongFunction);
+ method public static void setAll(double[], java.util.function.IntToDoubleFunction);
method public static void sort(int[]);
method public static void sort(int[], int, int);
method public static void sort(long[]);
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 8bcbf51..7590325 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -305,7 +305,7 @@
}
if (zygote) {
- PreloadPublicNativeLibraries();
+ InitializeNativeLoader();
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a0a599e..cc1d68e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -744,7 +744,7 @@
Activity mParent;
boolean mCalled;
/*package*/ boolean mResumed;
- private boolean mStopped;
+ /*package*/ boolean mStopped;
boolean mFinished;
boolean mStartedActivity;
private boolean mDestroyed;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4f9f41d..dbc6c84 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -379,6 +379,33 @@
? "no component name" : componentName.toShortString())
+ "}";
}
+
+ public String getStateString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("ActivityClientRecord{");
+ sb.append("paused=").append(paused);
+ sb.append(", stopped=").append(stopped);
+ sb.append(", hideForNow=").append(hideForNow);
+ sb.append(", startsNotResumed=").append(startsNotResumed);
+ sb.append(", isForward=").append(isForward);
+ sb.append(", pendingConfigChanges=").append(pendingConfigChanges);
+ sb.append(", onlyLocalRequest=").append(onlyLocalRequest);
+ sb.append(", preserveWindow=").append(mPreserveWindow);
+ if (activity != null) {
+ sb.append(", Activity{");
+ sb.append("resumed=").append(activity.mResumed);
+ sb.append(", stopped=").append(activity.mStopped);
+ sb.append(", finished=").append(activity.isFinishing());
+ sb.append(", destroyed=").append(activity.isDestroyed());
+ sb.append(", startedActivity=").append(activity.mStartedActivity);
+ sb.append(", temporaryPause=").append(activity.mTemporaryPause);
+ sb.append(", changingConfigurations=").append(activity.mChangingConfigurations);
+ sb.append(", visibleBehind=").append(activity.mVisibleBehind);
+ sb.append("}");
+ }
+ sb.append("}");
+ return sb.toString();
+ }
}
final class ProviderClientRecord {
@@ -3752,9 +3779,10 @@
return;
}
RuntimeException e = new RuntimeException(
- "Performing stop of activity that is not resumed: "
+ "Performing stop of activity that is already stopped: "
+ r.intent.getComponent().toShortString());
Slog.e(TAG, e.getMessage(), e);
+ Slog.e(TAG, r.getStateString());
}
if (info != null) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 132b6dd..e9d0846 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3197,6 +3197,7 @@
}
private void resetStandardTemplateWithActions(RemoteViews big) {
+ big.setViewVisibility(R.id.actions_container, View.GONE);
big.setViewVisibility(R.id.actions, View.GONE);
big.removeAllViews(R.id.actions);
@@ -3218,6 +3219,7 @@
int N = mActions.size();
if (N > 0) {
+ big.setViewVisibility(R.id.actions_container, View.VISIBLE);
big.setViewVisibility(R.id.actions, View.VISIBLE);
if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
for (int i=0; i<N; i++) {
@@ -4580,13 +4582,13 @@
private void buildIntoRemoteViewContent(RemoteViews remoteViews,
RemoteViews customContent) {
- remoteViews.removeAllViews(R.id.notification_main_column);
- // Need to clone customContent before adding, because otherwise it can no longer be
- // parceled independently of remoteViews.
if (customContent != null) {
+ // Need to clone customContent before adding, because otherwise it can no longer be
+ // parceled independently of remoteViews.
customContent = customContent.clone();
+ remoteViews.removeAllViews(R.id.notification_main_column);
+ remoteViews.addView(R.id.notification_main_column, customContent);
}
- remoteViews.addView(R.id.notification_main_column, customContent);
// also update the end margin if there is an image
int endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
R.dimen.notification_content_margin_end);
@@ -4689,13 +4691,13 @@
private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
RemoteViews customContent) {
- remoteViews.removeAllViews(id);
- // Need to clone customContent before adding, because otherwise it can no longer be
- // parceled independently of remoteViews.
if (customContent != null) {
+ // Need to clone customContent before adding, because otherwise it can no longer be
+ // parceled independently of remoteViews.
customContent = customContent.clone();
+ remoteViews.removeAllViews(id);
+ remoteViews.addView(id, customContent);
}
- remoteViews.addView(id, customContent);
return remoteViews;
}
}
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index bbfec41..828ac38 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -73,9 +73,32 @@
public static final int BACKOFF_POLICY_EXPONENTIAL = 1;
/* Minimum interval for a periodic job, in milliseconds. */
- public static final long MIN_PERIOD_MILLIS = 60 * 60 * 1000L; // 60 minutes
+ private static final long MIN_PERIOD_MILLIS = 15 * 60 * 1000L; // 15 minutes
+
/* Minimum flex for a periodic job, in milliseconds. */
- public static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes
+ private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes
+
+ /**
+ * Query the minimum interval allowed for periodic scheduled jobs. Attempting
+ * to declare a smaller period that this when scheduling a job will result in a
+ * job that is still periodic, but will run with this effective period.
+ *
+ * @return The minimum available interval for scheduling periodic jobs, in milliseconds.
+ */
+ public static final long getMinimumPeriod() {
+ return MIN_PERIOD_MILLIS;
+ }
+
+ /**
+ * Query the minimum flex time allowed for periodic scheduled jobs. Attempting
+ * to declare a shorter flex time than this when scheduling such a job will
+ * result in this amount as the effective flex time for the job.
+ *
+ * @return The minimum available flex time for scheduling periodic jobs, in milliseconds.
+ */
+ public static final long getMinimumFlex() {
+ return MIN_FLEX_MILLIS;
+ }
/**
* Default type of backoff.
@@ -227,7 +250,7 @@
* job does not recur periodically.
*/
public long getIntervalMillis() {
- return intervalMillis >= MIN_PERIOD_MILLIS ? intervalMillis : MIN_PERIOD_MILLIS;
+ return intervalMillis >= getMinimumPeriod() ? intervalMillis : getMinimumPeriod();
}
/**
@@ -236,7 +259,7 @@
public long getFlexMillis() {
long interval = getIntervalMillis();
long percentClamp = 5 * interval / 100;
- long clampedFlex = Math.max(flexMillis, Math.max(percentClamp, MIN_FLEX_MILLIS));
+ long clampedFlex = Math.max(flexMillis, Math.max(percentClamp, getMinimumFlex()));
return clampedFlex <= interval ? clampedFlex : interval;
}
@@ -565,9 +588,9 @@
* Specify that this job should recur with the provided interval and flex. The job can
* execute at any time in a window of flex length at the end of the period.
* @param intervalMillis Millisecond interval for which this job will repeat. A minimum
- * value of {@link #MIN_PERIOD_MILLIS} is enforced.
+ * value of {@link #getMinimumPeriod()} is enforced.
* @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
- * {@link #MIN_FLEX_MILLIS} or 5 percent of the period, whichever is
+ * {@link #getMinimumFlex()} or 5 percent of the period, whichever is
* higher.
*/
public Builder setPeriodic(long intervalMillis, long flexMillis) {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 56dcdb7..ed8143e 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1057,8 +1057,13 @@
}
/** {@hide} */
- public void setInstallFlagsDontKillApp() {
- installFlags |= PackageManager.INSTALL_DONT_KILL_APP;
+ @SystemApi
+ public void setDontKillApp(boolean dontKillApp) {
+ if (dontKillApp) {
+ installFlags |= PackageManager.INSTALL_DONT_KILL_APP;
+ } else {
+ installFlags &= ~PackageManager.INSTALL_DONT_KILL_APP;
+ }
}
/** {@hide} */
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index e41136c..b5c1f30 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -524,7 +524,7 @@
}
/** @hide*/
- public void setFlags(@ShortcutFlags int flags) {
+ public void replaceFlags(@ShortcutFlags int flags) {
mFlags = flags;
}
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index 2dfb061..8582150 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -43,6 +43,16 @@
public final RssiCurve rssiCurve;
/**
+ * A boolean value that indicates whether or not the network is believed to be metered.
+ *
+ * <p>A network can be classified as metered if the user would be
+ * sensitive to heavy data usage on that connection due to monetary costs,
+ * data limitations or battery/performance issues. A typical example would
+ * be a wifi connection where the user would be charged for usage.
+ */
+ public final boolean meteredHint;
+
+ /**
* Construct a new {@link ScoredNetwork}.
*
* @param networkKey the {@link NetworkKey} uniquely identifying this network.
@@ -54,8 +64,26 @@
* the scorer may choose to issue an out-of-band update at any time.
*/
public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve) {
+ this(networkKey, rssiCurve, false /* meteredHint */);
+ }
+
+ /**
+ * Construct a new {@link ScoredNetwork}.
+ *
+ * @param networkKey the {@link NetworkKey} uniquely identifying this network.
+ * @param rssiCurve the {@link RssiCurve} representing the scores for this network based on the
+ * RSSI. This field is optional, and may be skipped to represent a network which the scorer
+ * has opted not to score at this time. Passing a null value here is strongly preferred to
+ * not returning any {@link ScoredNetwork} for a given {@link NetworkKey} because it
+ * indicates to the system not to request scores for this network in the future, although
+ * the scorer may choose to issue an out-of-band update at any time.
+ * @param meteredHint A boolean value indicating whether or not the network is believed to be
+ * metered.
+ */
+ public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint) {
this.networkKey = networkKey;
this.rssiCurve = rssiCurve;
+ this.meteredHint = meteredHint;
}
private ScoredNetwork(Parcel in) {
@@ -65,6 +93,7 @@
} else {
rssiCurve = null;
}
+ meteredHint = in.readByte() != 0;
}
@Override
@@ -81,6 +110,7 @@
} else {
out.writeByte((byte) 0);
}
+ out.writeByte((byte) (meteredHint ? 1 : 0));
}
@Override
@@ -90,18 +120,20 @@
ScoredNetwork that = (ScoredNetwork) o;
- return Objects.equals(networkKey, that.networkKey) &&
- Objects.equals(rssiCurve, that.rssiCurve);
+ return Objects.equals(networkKey, that.networkKey)
+ && Objects.equals(rssiCurve, that.rssiCurve)
+ && Objects.equals(meteredHint, that.meteredHint);
}
@Override
public int hashCode() {
- return Objects.hash(networkKey, rssiCurve);
+ return Objects.hash(networkKey, rssiCurve, meteredHint);
}
@Override
public String toString() {
- return "ScoredNetwork[key=" + networkKey + ",score=" + rssiCurve + "]";
+ return "ScoredNetwork[key=" + networkKey + ",score=" + rssiCurve
+ + ",meteredHint=" + meteredHint + "]";
}
public static final Parcelable.Creator<ScoredNetwork> CREATOR =
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 2445bc2..1ec00db 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -38,6 +38,8 @@
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
+import com.android.internal.annotations.GuardedBy;
+
/**
* Turns a {@link SeekBar} into a volume control.
* @hide
@@ -67,6 +69,10 @@
private Observer mVolumeObserver;
private int mOriginalStreamVolume;
private int mLastAudibleStreamVolume;
+ // When the old handler is destroyed and a new one is created, there could be a situation where
+ // this is accessed at the same time in different handlers. So, access to this field needs to be
+ // synchronized.
+ @GuardedBy("this")
private Ringtone mRingtone;
private int mLastProgress = -1;
private boolean mMuted;
@@ -174,9 +180,11 @@
}
private void onInitSample() {
- mRingtone = RingtoneManager.getRingtone(mContext, mDefaultUri);
- if (mRingtone != null) {
- mRingtone.setStreamType(mStreamType);
+ synchronized (this) {
+ mRingtone = RingtoneManager.getRingtone(mContext, mDefaultUri);
+ if (mRingtone != null) {
+ mRingtone.setStreamType(mStreamType);
+ }
}
}
@@ -192,16 +200,19 @@
if (mCallback != null) {
mCallback.onSampleStarting(this);
}
- if (mRingtone != null) {
- try {
- mRingtone.setAudioAttributes(new AudioAttributes.Builder(mRingtone
- .getAudioAttributes())
- .setFlags(AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
- AudioAttributes.FLAG_BYPASS_MUTE)
- .build());
- mRingtone.play();
- } catch (Throwable e) {
- Log.w(TAG, "Error playing ringtone, stream " + mStreamType, e);
+
+ synchronized (this) {
+ if (mRingtone != null) {
+ try {
+ mRingtone.setAudioAttributes(new AudioAttributes.Builder(mRingtone
+ .getAudioAttributes())
+ .setFlags(AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
+ AudioAttributes.FLAG_BYPASS_MUTE)
+ .build());
+ mRingtone.play();
+ } catch (Throwable e) {
+ Log.w(TAG, "Error playing ringtone, stream " + mStreamType, e);
+ }
}
}
}
@@ -216,8 +227,10 @@
}
private void onStopSample() {
- if (mRingtone != null) {
- mRingtone.stop();
+ synchronized (this) {
+ if (mRingtone != null) {
+ mRingtone.stop();
+ }
}
}
@@ -274,7 +287,9 @@
}
public boolean isSamplePlaying() {
- return mRingtone != null && mRingtone.isPlaying();
+ synchronized (this) {
+ return mRingtone != null && mRingtone.isPlaying();
+ }
}
public void startSample() {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 049d585..5fc2899 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2093,7 +2093,7 @@
*/
public static void clearConfiguration(Configuration inoutConfig) {
inoutConfig.fontScale = 0;
- if (!inoutConfig.userSetLocale) {
+ if (!inoutConfig.userSetLocale && !inoutConfig.getLocales().isEmpty()) {
inoutConfig.setLocales(LocaleList.getEmptyLocaleList());
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2eb258f..b16508e 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -32,6 +32,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.StrictMode;
@@ -2622,6 +2623,16 @@
mProvider.getViewDelegate().onFinishTemporaryDetach();
}
+ @Override
+ public Handler getHandler() {
+ return mProvider.getViewDelegate().getHandler(super.getHandler());
+ }
+
+ @Override
+ public View findFocus() {
+ return mProvider.getViewDelegate().findFocus(super.findFocus());
+ }
+
/**
* Receive the result from a previous call to {@link #startActivityForResult(Intent, int)}.
*
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 94d231c..e5b65e7 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -28,6 +28,7 @@
import android.net.http.SslCertificate;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Message;
import android.print.PrintDocumentAdapter;
import android.view.DragEvent;
@@ -386,6 +387,10 @@
public void onFinishTemporaryDetach();
public void onActivityResult(int requestCode, int resultCode, Intent data);
+
+ public Handler getHandler(Handler originalHandler);
+
+ public View findFocus(View originalFocusedView);
}
interface ScrollDelegate {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8a1a8c5..5060350 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9646,7 +9646,10 @@
}
boolean canShare() {
- return canCopy() && isDeviceProvisioned();
+ if (!getContext().canStartActivityForResult() || !isDeviceProvisioned()) {
+ return false;
+ }
+ return canCopy();
}
boolean isDeviceProvisioned() {
@@ -9669,16 +9672,10 @@
}
boolean canProcessText() {
- if (!getContext().canStartActivityForResult() || getId() == View.NO_ID
- || hasPasswordTransformationMethod()) {
+ if (getId() == View.NO_ID) {
return false;
}
-
- if (mText.length() > 0 && hasSelection() && mEditor != null) {
- return true;
- }
-
- return false;
+ return canShare();
}
boolean canSelectAllText() {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 9461ce6..f3ae688 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -909,14 +909,6 @@
return imsList;
}
- @Deprecated
- public InputMethodSettings(
- Resources res, ContentResolver resolver,
- HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
- @UserIdInt int userId) {
- this(res, resolver, methodMap, methodList, userId, false /* copyOnWrite */);
- }
-
public InputMethodSettings(
Resources res, ContentResolver resolver,
HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index a9003c1..a7ac5b8 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -28,6 +28,7 @@
#include <DamageAccumulator.h>
#include <Matrix.h>
#include <RenderNode.h>
+#include <renderthread/CanvasContext.h>
#include <TreeInfo.h>
#include <Paint.h>
@@ -487,15 +488,7 @@
virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override {
if (CC_UNLIKELY(!mWeakRef || !info.updateWindowPositions)) return;
- ATRACE_NAME("Update SurfaceView position");
- JNIEnv* env = jnienv();
- jobject localref = env->NewLocalRef(mWeakRef);
- if (CC_UNLIKELY(!localref)) {
- jnienv()->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
- return;
- }
Matrix4 transform;
info.damageAccumulator->computeCurrentTransform(&transform);
const RenderProperties& props = node.properties();
@@ -505,10 +498,13 @@
bounds.right -= info.windowInsetLeft;
bounds.top -= info.windowInsetTop;
bounds.bottom -= info.windowInsetTop;
- env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod,
- (jlong) info.frameNumber, (jint) bounds.left, (jint) bounds.top,
- (jint) bounds.right, (jint) bounds.bottom);
- env->DeleteLocalRef(localref);
+
+ auto functor = std::bind(
+ std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePosition), this,
+ (jlong) info.frameNumber, (jint) bounds.left, (jint) bounds.top,
+ (jint) bounds.right, (jint) bounds.bottom);
+
+ info.canvasContext.enqueueFrameWork(std::move(functor));
}
private:
@@ -520,6 +516,23 @@
return env;
}
+ void doUpdatePosition(jlong frameNumber, jint left, jint top,
+ jint right, jint bottom) {
+ ATRACE_NAME("Update SurfaceView position");
+
+ JNIEnv* env = jnienv();
+ jobject localref = env->NewLocalRef(mWeakRef);
+ if (CC_UNLIKELY(!localref)) {
+ jnienv()->DeleteWeakGlobalRef(mWeakRef);
+ mWeakRef = nullptr;
+ return;
+ }
+
+ env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod,
+ frameNumber, left, top, right, bottom);
+ env->DeleteLocalRef(localref);
+ }
+
JavaVM* mVm;
jobject mWeakRef;
};
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index a65a813..e636bc0 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -993,7 +993,7 @@
</style>
<style name="Widget.Material.SuggestionItem" parent="@android:style/TextAppearance.Material.Body1">
- <item name="textColor">#8a000000</item> <!-- alpha=.54, textColor=@color/black -->
+ <item name="textColor">?attr/textColorSecondary</item>
<item name="drawablePadding">8dip</item>
<item name="gravity">start|center_vertical</item>
<item name="layout_gravity">start|center_vertical</item>
@@ -1008,11 +1008,11 @@
</style>
<style name="TextAppearance.Material.TextSuggestionHighlight" parent="Widget.Material.SuggestionItem">
- <item name="textColor">#de000000</item> <!-- alpha=.87, textColor=@color/black -->
+ <item name="textColor">?attr/textColorPrimary</item>
</style>
<style name="Widget.Material.SuggestionButton" parent="@android:style/TextAppearance.Material.Button">
- <item name="textColor">#de009688</item> <!-- alpha=.87, textColor=#009688 -->
+ <item name="textColor">?attr/colorAccent</item>
<item name="drawablePadding">8dip</item>
<item name="gravity">start|center_vertical</item>
<item name="layout_gravity">start|center_vertical</item>
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
index cbf17a4..ec8cd71 100644
--- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -37,6 +37,8 @@
import android.print.mockservice.PrinterDiscoverySessionCallbacks;
import android.print.mockservice.StubbablePrinterDiscoverySession;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -253,6 +255,7 @@
/**
* test IPrintManager.getPrintJobInfo
*/
+ @LargeTest
public void testGetPrintJobInfo() throws Exception {
startPrinting();
@@ -274,6 +277,7 @@
/**
* test IPrintManager.getPrintJobInfos
*/
+ @LargeTest
public void testGetPrintJobInfos() throws Exception {
startPrinting();
@@ -301,6 +305,7 @@
/**
* test IPrintManager.print
*/
+ @LargeTest
public void testPrint() throws Exception {
final String name = "dummy print job";
@@ -354,6 +359,7 @@
/**
* test IPrintManager.cancelPrintJob
*/
+ @LargeTest
public void testCancelPrintJob() throws Exception {
startPrinting();
@@ -377,6 +383,7 @@
/**
* test IPrintManager.restartPrintJob
*/
+ @LargeTest
public void testRestartPrintJob() throws Exception {
startPrinting();
@@ -399,6 +406,7 @@
/**
* test IPrintManager.addPrintJobStateChangeListener
*/
+ @MediumTest
public void testAddPrintJobStateChangeListener() throws Exception {
final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener();
@@ -424,6 +432,7 @@
/**
* test IPrintManager.removePrintJobStateChangeListener
*/
+ @MediumTest
public void testRemovePrintJobStateChangeListener() throws Exception {
final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener();
@@ -447,6 +456,7 @@
/**
* test IPrintManager.addPrintServicesChangeListener
*/
+ @MediumTest
public void testAddPrintServicesChangeListener() throws Exception {
final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
@@ -465,6 +475,7 @@
/**
* test IPrintManager.removePrintServicesChangeListener
*/
+ @MediumTest
public void testRemovePrintServicesChangeListener() throws Exception {
final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
@@ -488,6 +499,7 @@
/**
* test IPrintManager.getPrintServices
*/
+ @MediumTest
public void testGetPrintServices() throws Exception {
List<PrintServiceInfo> printServices = mIPrintManager.getPrintServices(
PrintManager.ALL_SERVICES, mUserId);
@@ -509,6 +521,7 @@
/**
* test IPrintManager.setPrintServiceEnabled
*/
+ @MediumTest
public void testSetPrintServiceEnabled() throws Exception {
final ComponentName printService = mIPrintManager.getPrintServices(
PrintManager.ALL_SERVICES, mUserId).get(0).getComponentName();
@@ -548,6 +561,7 @@
/**
* test IPrintManager.createPrinterDiscoverySession
*/
+ @MediumTest
public void testCreatePrinterDiscoverySession() throws Exception {
final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
@@ -573,6 +587,7 @@
/**
* test IPrintManager.startPrinterDiscovery
*/
+ @LargeTest
public void testStartPrinterDiscovery() throws Exception {
startPrinting();
@@ -615,6 +630,7 @@
/**
* test IPrintManager.stopPrinterDiscovery
*/
+ @MediumTest
public void testStopPrinterDiscovery() throws Exception {
final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
@@ -638,6 +654,7 @@
/**
* test IPrintManager.validatePrinters
*/
+ @LargeTest
public void testValidatePrinters() throws Exception {
startPrinting();
@@ -678,6 +695,7 @@
/**
* test IPrintManager.startPrinterStateTracking
*/
+ @LargeTest
public void testStartPrinterStateTracking() throws Exception {
startPrinting();
@@ -699,6 +717,7 @@
/**
* test IPrintManager.getCustomPrinterIcon
*/
+ @LargeTest
public void testGetCustomPrinterIcon() throws Exception {
startPrinting();
@@ -720,6 +739,7 @@
/**
* test IPrintManager.stopPrinterStateTracking
*/
+ @LargeTest
public void testStopPrinterStateTracking() throws Exception {
startPrinting();
@@ -746,6 +766,7 @@
/**
* test IPrintManager.destroyPrinterDiscoverySession
*/
+ @MediumTest
public void testDestroyPrinterDiscoverySession() throws Exception {
final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 3e86e6f..3973f2f 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -17,10 +17,13 @@
package android.graphics;
import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.graphics.drawable.Drawable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Defines a simple shape, used for bounding graphical regions.
* <p>
@@ -34,11 +37,28 @@
public final class Outline {
private static final float RADIUS_UNDEFINED = Float.NEGATIVE_INFINITY;
- /** @hide */
- public Path mPath;
+ private static final int MODE_EMPTY = 0;
+ private static final int MODE_RECT = 1;
+ private static final int MODE_CONVEX_PATH = 2;
/** @hide */
- public Rect mRect;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false,
+ value = {
+ MODE_EMPTY,
+ MODE_RECT,
+ MODE_CONVEX_PATH,
+ })
+ public @interface Mode {}
+
+ @Mode
+ private int mMode = MODE_EMPTY;
+
+ /** @hide */
+ public final Path mPath = new Path();
+
+ /** @hide */
+ public final Rect mRect = new Rect();
/** @hide */
public float mRadius = RADIUS_UNDEFINED;
/** @hide */
@@ -63,8 +83,9 @@
* @see #isEmpty()
*/
public void setEmpty() {
- mPath = null;
- mRect = null;
+ mMode = MODE_EMPTY;
+ mPath.rewind();
+ mRect.setEmpty();
mRadius = RADIUS_UNDEFINED;
}
@@ -77,7 +98,7 @@
* @see #setEmpty()
*/
public boolean isEmpty() {
- return mRect == null && mPath == null;
+ return mMode == MODE_EMPTY;
}
@@ -90,7 +111,7 @@
* @see {@link android.view.View#setClipToOutline(boolean)}
*/
public boolean canClip() {
- return !isEmpty() && mRect != null;
+ return mMode != MODE_CONVEX_PATH;
}
/**
@@ -122,19 +143,9 @@
* @param src Source outline to copy from.
*/
public void set(@NonNull Outline src) {
- if (src.mPath != null) {
- if (mPath == null) {
- mPath = new Path();
- }
- mPath.set(src.mPath);
- mRect = null;
- }
- if (src.mRect != null) {
- if (mRect == null) {
- mRect = new Rect();
- }
- mRect.set(src.mRect);
- }
+ mMode = src.mMode;
+ mPath.set(src.mPath);
+ mRect.set(src.mRect);
mRadius = src.mRadius;
mAlpha = src.mAlpha;
}
@@ -165,10 +176,10 @@
return;
}
- if (mRect == null) mRect = new Rect();
+ mMode = MODE_RECT;
mRect.set(left, top, right, bottom);
mRadius = radius;
- mPath = null;
+ mPath.rewind();
}
/**
@@ -188,7 +199,7 @@
* bounds, or {@code false} if no outline bounds are set
*/
public boolean getRect(@NonNull Rect outRect) {
- if (mRect == null) {
+ if (mMode != MODE_RECT) {
return false;
}
outRect.set(mRect);
@@ -221,10 +232,10 @@
return;
}
- if (mPath == null) mPath = new Path();
- mPath.reset();
+ mMode = MODE_CONVEX_PATH;
+ mPath.rewind();
mPath.addOval(left, top, right, bottom, Path.Direction.CW);
- mRect = null;
+ mRect.setEmpty();
mRadius = RADIUS_UNDEFINED;
}
@@ -248,10 +259,10 @@
if (!convexPath.isConvex()) {
throw new IllegalArgumentException("path must be convex");
}
- if (mPath == null) mPath = new Path();
+ mMode = MODE_CONVEX_PATH;
mPath.set(convexPath);
- mRect = null;
+ mRect.setEmpty();
mRadius = RADIUS_UNDEFINED;
}
@@ -259,9 +270,9 @@
* Offsets the Outline by (dx,dy)
*/
public void offset(int dx, int dy) {
- if (mRect != null) {
+ if (mMode == MODE_RECT) {
mRect.offset(dx, dy);
- } else if (mPath != null) {
+ } else if (mMode == MODE_CONVEX_PATH) {
mPath.offset(dx, dy);
}
}
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 330dc29..eac9359 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -92,6 +92,8 @@
*/
bool init();
+ bool isInitialized() { return mInitialized; }
+
/**
* Flush the cache.
*
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index fd5856a..50b21a4 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -646,7 +646,9 @@
}
void FrameBuilder::deferTextOp(const TextOp& op) {
- BakedOpState* bakedState = tryBakeOpState(op);
+ BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
+ mAllocator, *mCanvasState.writableSnapshot(), op,
+ BakedOpState::StrokeBehavior::StyleDefined);
if (!bakedState) return; // quick rejected
batchid_t batchId = textBatchId(*(op.paint));
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 114347d..cdbbbab 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -197,7 +197,12 @@
}
void Layer::clearTexture() {
- caches.textureState().unbindTexture(texture.mId);
+ // There's a rare possibility that Caches could have been destroyed already
+ // since this method is queued up as a task.
+ // Since this is a reset method, treat this as non-fatal.
+ if (caches.isInitialized()) {
+ caches.textureState().unbindTexture(texture.mId);
+ }
texture.mId = 0;
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index a496b49..c539d63 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -484,6 +484,8 @@
bool drew = mCanvas->finish();
#endif
+ waitOnFences();
+
GL_CHECKPOINT(LOW);
// Even if we decided to cancel the frame, from the perspective of jank
@@ -726,6 +728,37 @@
#endif
}
+void CanvasContext::waitOnFences() {
+ if (mFrameFences.size()) {
+ ATRACE_CALL();
+ for (auto& fence : mFrameFences) {
+ fence->getResult();
+ }
+ mFrameFences.clear();
+ }
+}
+
+class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> {
+public:
+ FuncTaskProcessor(Caches& caches)
+ : TaskProcessor<bool>(&caches.tasks) {}
+
+ virtual void onProcess(const sp<Task<bool> >& task) override {
+ FuncTask* t = static_cast<FuncTask*>(task.get());
+ t->func();
+ task->setResult(true);
+ }
+};
+
+void CanvasContext::enqueueFrameWork(std::function<void()>&& func) {
+ if (!mFrameWorkProcessor.get()) {
+ mFrameWorkProcessor = new FuncTaskProcessor(Caches::getInstance());
+ }
+ sp<FuncTask> task(new FuncTask());
+ task->func = func;
+ mFrameWorkProcessor->add(task);
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index cb61e51..6706c30 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -24,6 +24,8 @@
#include "IContextFactory.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
+#include "thread/Task.h"
+#include "thread/TaskProcessor.h"
#include "utils/RingBuffer.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
@@ -41,6 +43,7 @@
#include <utils/Functor.h>
#include <gui/Surface.h>
+#include <functional>
#include <set>
#include <string>
#include <vector>
@@ -159,6 +162,9 @@
}
}
+ // Used to queue up work that needs to be completed before this frame completes
+ ANDROID_API void enqueueFrameWork(std::function<void()>&& func);
+
private:
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
@@ -170,6 +176,8 @@
void freePrefetechedLayers();
+ void waitOnFences();
+
EGLint mLastFrameWidth = 0;
EGLint mLastFrameHeight = 0;
@@ -213,6 +221,16 @@
// Stores the bounds of the main content.
Rect mContentDrawBounds;
+
+ // TODO: This is really a Task<void> but that doesn't really work
+ // when Future<> expects to be able to get/set a value
+ struct FuncTask : public Task<bool> {
+ std::function<void()> func;
+ };
+ class FuncTaskProcessor;
+
+ std::vector< sp<FuncTask> > mFrameFences;
+ sp<TaskProcessor<bool> > mFrameWorkProcessor;
};
} /* namespace renderthread */
diff --git a/libs/hwui/tests/scripts/prep_volantis.sh b/libs/hwui/tests/scripts/prep_volantis.sh
index 09d4869..0572ee55 100755
--- a/libs/hwui/tests/scripts/prep_volantis.sh
+++ b/libs/hwui/tests/scripts/prep_volantis.sh
@@ -49,6 +49,6 @@
# 684000 708000 756000 804000 852000 (kHz)
S=324000000
-echo "set gpu to $s hz"
+echo "set gpu to $S hz"
adb shell "echo 1 > /d/clock/override.gbus/state"
adb shell "echo $S > /d/clock/override.gbus/rate"
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index f147fd4..31555f2 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -316,6 +316,61 @@
<< "Expect number of ops = 2 * loop count";
}
+static auto styles = {
+ SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
+
+TEST(FrameBuilder, textStyle) {
+ class TextStyleTestRenderer : public TestRendererBase {
+ public:
+ void onMergedTextOps(const MergedBakedOpList& opList) override {
+ ASSERT_EQ(0, mIndex);
+ ASSERT_EQ(3u, opList.count);
+ mIndex += opList.count;
+
+ int index = 0;
+ for (auto style : styles) {
+ auto state = opList.states[index++];
+ ASSERT_EQ(style, state->op->paint->getStyle())
+ << "Remainder of validation relies upon stable merged order";
+ ASSERT_EQ(0, state->computedState.clipSideFlags)
+ << "Clipped bounds validation requires unclipped ops";
+ }
+
+ Rect fill = opList.states[0]->computedState.clippedBounds;
+ Rect stroke = opList.states[1]->computedState.clippedBounds;
+ EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
+ << "Stroke+Fill should be same as stroke";
+
+ EXPECT_TRUE(stroke.contains(fill));
+ EXPECT_FALSE(fill.contains(stroke));
+
+ Rect outsetFill(fill);
+ outsetFill.outset(10);
+ EXPECT_EQ(stroke, outsetFill);
+ }
+ };
+ auto node = TestUtils::createNode(0, 0, 400, 400,
+ [](RenderProperties& props, TestCanvas& canvas) {
+ SkPaint paint;
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint.setAntiAlias(true);
+ paint.setTextSize(50);
+ paint.setStrokeWidth(10);
+
+ // draw 3 copies of the same text overlapping, each with a different style.
+ // They'll get merged, but with
+ for (auto style : styles) {
+ paint.setStyle(style);
+ TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100);
+ }
+ });
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
+ TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+ TextStyleTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
+}
+
RENDERTHREAD_TEST(FrameBuilder, textureLayer) {
class TextureLayerTestRenderer : public TestRendererBase {
public:
diff --git a/libs/hwui/thread/Barrier.h b/libs/hwui/thread/Barrier.h
index 6cb23e5..0a7acb0 100644
--- a/libs/hwui/thread/Barrier.h
+++ b/libs/hwui/thread/Barrier.h
@@ -33,11 +33,6 @@
mCondition.signal(mType);
}
- void close() {
- Mutex::Autolock l(mLock);
- mOpened = false;
- }
-
void wait() const {
Mutex::Autolock l(mLock);
while (!mOpened) {
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 11fecfb..a7eb73f 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -47,7 +47,6 @@
private double mCarrierPhaseUncertainty;
private int mMultipathIndicator;
private double mSnrInDb;
- private boolean mPseudorangeRateCorrected;
// The following enumerations must be in sync with the values declared in gps.h
@@ -440,25 +439,6 @@
}
/**
- * See {@link #getPseudorangeRateMetersPerSecond()} for more details.
- *
- * @return {@code true} if {@link #getPseudorangeRateMetersPerSecond()} contains a corrected
- * value, {@code false} if it contains an uncorrected value.
- */
- public boolean isPseudorangeRateCorrected() {
- return mPseudorangeRateCorrected;
- }
-
- /**
- * Sets whether the pseudorange corrected.
- * @hide
- */
- @TestApi
- public void setPseudorangeRateCorrected(boolean value) {
- mPseudorangeRateCorrected = value;
- }
-
- /**
* Gets the pseudorange's rate uncertainty (1-Sigma) in m/s.
* The uncertainty is represented as an absolute (single sided) value.
*/
@@ -813,7 +793,6 @@
gnssMeasurement.mCarrierPhaseUncertainty = parcel.readDouble();
gnssMeasurement.mMultipathIndicator = parcel.readInt();
gnssMeasurement.mSnrInDb = parcel.readDouble();
- gnssMeasurement.mPseudorangeRateCorrected = (parcel.readByte() != 0);
return gnssMeasurement;
}
@@ -845,7 +824,6 @@
parcel.writeDouble(mCarrierPhaseUncertainty);
parcel.writeInt(mMultipathIndicator);
parcel.writeDouble(mSnrInDb);
- parcel.writeByte((byte) (mPseudorangeRateCorrected ? 1 : 0));
}
@Override
@@ -880,10 +858,6 @@
mPseudorangeRateMetersPerSecond,
"PseudorangeRateUncertaintyMetersPerSecond",
mPseudorangeRateUncertaintyMetersPerSecond));
- builder.append(String.format(
- format,
- "PseudorangeRateIsCorrected",
- isPseudorangeRateCorrected()));
builder.append(String.format(
format,
@@ -943,7 +917,6 @@
resetCarrierPhaseUncertainty();
setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
resetSnrInDb();
- setPseudorangeRateCorrected(false);
}
private void setFlag(int flag) {
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index ac255c8..e5c0f27 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -286,18 +286,11 @@
navigationMessage.setSvid(parcel.readInt());
navigationMessage.setMessageId(parcel.readInt());
navigationMessage.setSubmessageId(parcel.readInt());
-
int dataLength = parcel.readInt();
byte[] data = new byte[dataLength];
parcel.readByteArray(data);
navigationMessage.setData(data);
-
- if (parcel.dataAvail() >= Integer.SIZE) {
- int status = parcel.readInt();
- navigationMessage.setStatus(status);
- } else {
- navigationMessage.setStatus(STATUS_UNKNOWN);
- }
+ navigationMessage.setStatus(parcel.readInt());
return navigationMessage;
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d179171..3c42161 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1076,9 +1076,6 @@
* <p>
* This method has no effect if the device implements a fixed volume policy
* as indicated by {@link #isVolumeFixed()}.
- * <p>From N onward, stream mute changes that would toggle Do Not Disturb are not allowed unless
- * the app has been granted Do Not Disturb Access.
- * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
* <p>
* This method was deprecated in API level 22. Prior to API level 22 this
* method had significantly different behavior and should be used carefully.
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 26061e4..346f083 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -508,7 +508,8 @@
* The associated value is an integer.
* Constants are declared in {@link MediaCodecInfo.CodecProfileLevel}.
* This key is used as a hint, and is only supported for codecs
- * that specify a profile.
+ * that specify a profile. Note: Codecs are free to use all the available
+ * coding tools at the specified profile.
*
* @see MediaCodecInfo.CodecCapabilities#profileLevels
*/
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index 76c701a..5cfe300 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -105,6 +105,14 @@
/*****************************************************************************/
+int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor,
+ int32_t samplingPeriodUs, int maxBatchReportLatencyUs)
+{
+ return static_cast<SensorEventQueue*>(queue)->enableSensor(
+ static_cast<Sensor const*>(sensor)->getHandle(), samplingPeriodUs,
+ maxBatchReportLatencyUs, 0);
+}
+
int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor)
{
return static_cast<SensorEventQueue*>(queue)->enableSensor(
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index 606f4eb..645e182 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -20,6 +20,8 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
+import android.os.Handler;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes.MediaSize;
@@ -570,7 +572,14 @@
if (DEBUG) {
Log.i(LOG_TAG, "Requesting pages: " + Arrays.toString(mRequestedPages));
}
- mCallbacks.onRequestContentUpdate();
+
+ // This call might come from a recylerview that is currently updating. Hence delay to
+ // after the update
+ (new Handler(Looper.getMainLooper())).post(new Runnable() {
+ @Override public void run() {
+ mCallbacks.onRequestContentUpdate();
+ }
+ });
}
}
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
index 3a7c1d1..567e009 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -18,7 +18,6 @@
android:id="@+id/recents_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/recents_tv_background_gradient"
android:clipChildren="false"
android:clipToPadding="false"
android:layoutDirection="rtl">
diff --git a/packages/SystemUI/res/layout/recents_tv_task_card_view.xml b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
index c5b1a7a..54e97da 100644
--- a/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
+++ b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
@@ -59,7 +59,7 @@
<ImageView
android:id="@+id/card_view_thumbnail"
android:layout_width="match_parent"
- android:layout_height="@dimen/recents_tv_card_height"
+ android:layout_height="@dimen/recents_tv_screenshot_height"
android:scaleType="centerCrop"
android:gravity="center"
android:layout_alignParentTop="true"
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 75195c4..7460201 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -43,6 +43,7 @@
android:singleLine="true"
android:ellipsize="start"
android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
+ android:textIsSelectable="true"
android:imeOptions="actionSend|flagNoExtractUi" />
<FrameLayout
@@ -62,6 +63,7 @@
android:paddingBottom="12dp"
android:id="@+id/remote_input_send"
android:src="@drawable/ic_send"
+ android:contentDescription="@*android:string/ime_action_send"
android:tint="@color/remote_input_send"
android:tintMode="src_in"
android:background="@drawable/ripple_drawable" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d65ab04..eeed0cf 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -155,6 +155,8 @@
<color name="default_remote_input_background">@*android:color/notification_default_color</color>
<color name="remote_input_hint">#4dffffff</color>
+ <color name="remote_input_accent">#eeeeee</color>
+
<color name="qs_tile_tint_unavailable">#40ffffff</color>
<color name="qs_tile_tint_inactive">#4dffffff</color>
<color name="qs_tile_tint_active">#ffffffff</color>
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index b589110..6b153d1 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -19,7 +19,7 @@
<resources>
<!-- Dimens for recents card in the recents view on tv -->
<dimen name="recents_tv_card_width">268dip</dimen>
- <dimen name="recents_tv_card_height">151dip</dimen>
+ <dimen name="recents_tv_screenshot_height">151dip</dimen>
<dimen name="recents_tv_card_extra_badge_size">20dip</dimen>
<dimen name="recents_tv_banner_width">114dip</dimen>
<dimen name="recents_tv_banner_height">64dip</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2660926..b0c1e95 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -201,8 +201,8 @@
<item name="android:colorControlActivated">@color/system_accent_color</item>
</style>
- <style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault">
- <item name="android:colorControlActivated">@android:color/white</item>
+ <style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault.Light">
+ <item name="android:colorAccent">@color/remote_input_accent</item>
</style>
<style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
diff --git a/packages/SystemUI/res/values/values_tv.xml b/packages/SystemUI/res/values/values_tv.xml
index 1fcc9e4..6a72e54 100644
--- a/packages/SystemUI/res/values/values_tv.xml
+++ b/packages/SystemUI/res/values/values_tv.xml
@@ -15,4 +15,5 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<item format="float" type="integer" name="unselected_scale">1.0</item>
+ <item format="float" type="integer" name="selected_scale">1.1</item>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 358674c..aa3f6e5 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -535,31 +535,43 @@
boolean nowExpanded;
int naturalHeight = mScaler.getNaturalHeight();
if (wasClosed) {
- nowExpanded = (force || currentHeight > mOldHeight);
+ nowExpanded = (force || currentHeight > mOldHeight && velocity >= 0);
} else {
- nowExpanded = !force && currentHeight >= mOldHeight;
+ nowExpanded = !force && (currentHeight >= mOldHeight || velocity > 0);
}
nowExpanded |= mNaturalHeight == mSmallSize;
if (mScaleAnimation.isRunning()) {
mScaleAnimation.cancel();
}
- mCallback.setUserExpandedChild(mResizedView, nowExpanded);
mCallback.expansionStateChanged(false);
float targetHeight = nowExpanded ? naturalHeight : mSmallSize;
if (targetHeight != currentHeight) {
mScaleAnimation.setFloatValues(targetHeight);
mScaleAnimation.setupStartValues();
final View scaledView = mResizedView;
+ final boolean expand = nowExpanded;
mScaleAnimation.addListener(new AnimatorListenerAdapter() {
+ public boolean mCancelled;
+
@Override
public void onAnimationEnd(Animator animation) {
+ if (!mCancelled) {
+ mCallback.setUserExpandedChild(scaledView, expand);
+ }
mCallback.setUserLockedChild(scaledView, false);
mScaleAnimation.removeListener(this);
}
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
});
+ velocity = nowExpanded == velocity >= 0 ? velocity : 0;
mFlingAnimationUtils.apply(mScaleAnimation, currentHeight, targetHeight, velocity);
mScaleAnimation.start();
} else {
+ mCallback.setUserExpandedChild(mResizedView, nowExpanded);
mCallback.setUserLockedChild(mResizedView, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index d864df8..9be24de 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -66,6 +66,7 @@
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskGrouping;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.tv.views.TaskCardView;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskStackView;
import com.android.systemui.recents.views.TaskStackViewScroller;
@@ -166,6 +167,7 @@
boolean mCanReuseTaskStackViews = true;
boolean mDraggingInRecents;
boolean mLaunchedWhileDocking;
+ private boolean mIsRunningOnTv;
// Task launching
Rect mSearchBarBounds = new Rect();
@@ -230,8 +232,10 @@
UiModeManager uiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
mRecentsIntentActivityName = RECENTS_TV_ACTIVITY;
+ mIsRunningOnTv = true;
} else {
mRecentsIntentActivityName = RECENTS_ACTIVITY;
+ mIsRunningOnTv = false;
}
}
@@ -793,6 +797,22 @@
}
}
+ /**
+ * Creates the activity options for an app->recents transition on TV.
+ */
+ private ActivityOptions getThumbnailTransitionActivityOptionsForTV(
+ ActivityManager.RunningTaskInfo topTask) {
+ Bitmap thumbnail = mThumbnailTransitionBitmapCache;
+ Rect rect = TaskCardView.getStartingCardThumbnailRect(mContext);
+ if (thumbnail != null) {
+ return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
+ null, (int) rect.left, (int) rect.top,
+ (int) rect.width(), (int) rect.height(), mHandler, null);
+ }
+ // If both the screenshot and thumbnail fails, then just fall back to the default transition
+ return getUnknownTransitionActivityOptions();
+ }
+
private Bitmap getThumbnailBitmap(ActivityManager.RunningTaskInfo topTask, Task toTask,
TaskViewTransform toTransform) {
Bitmap thumbnail;
@@ -872,6 +892,11 @@
boolean isTopTaskHome, boolean animate) {
RecentsTaskLoader loader = Recents.getTaskLoader();
+ // If we are on TV, divert to a different helper method
+ if (mIsRunningOnTv) {
+ setUpAndStartTvRecents(topTask, isTopTaskHome, animate);
+ return;
+ }
// In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
// should always preload the tasks now. If we are dragging in recents, reload them as
// the stacks might have changed.
@@ -947,6 +972,90 @@
}
/**
+ * Used to set up the animations of Tv Recents, then start the Recents Activity.
+ * TODO: Add the Transitions for Home -> Recents TV
+ * TODO: Shift Transition code to separate class under /tv directory and access
+ * from here
+ */
+ private void setUpAndStartTvRecents(ActivityManager.RunningTaskInfo topTask,
+ boolean isTopTaskHome, boolean animate) {
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+
+ // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
+ // should always preload the tasks now. If we are dragging in recents, reload them as
+ // the stacks might have changed.
+ if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) {
+ // Create a new load plan if preloadRecents() was never triggered
+ sInstanceLoadPlan = loader.createLoadPlan(mContext);
+ }
+ if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
+ loader.preloadTasks(sInstanceLoadPlan, topTask.id, isTopTaskHome);
+ }
+ TaskStack stack = sInstanceLoadPlan.getTaskStack();
+
+ // Update the header bar if necessary
+ updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
+
+ // Prepare the dummy stack for the transition
+ TaskStackLayoutAlgorithm.VisibilityReport stackVr =
+ mDummyStackView.computeStackVisibilityReport();
+
+ if (!animate) {
+ ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, -1, -1);
+ startRecentsActivity(topTask, opts, false /* fromHome */,
+ false /* fromSearchHome */, false /* fromThumbnail*/, stackVr);
+ return;
+ }
+
+ boolean hasRecentTasks = stack.getTaskCount() > 0;
+ boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
+
+ if (useThumbnailTransition) {
+ // Try starting with a thumbnail transition
+ ActivityOptions opts = getThumbnailTransitionActivityOptionsForTV(topTask);
+ if (opts != null) {
+ startRecentsActivity(topTask, opts, false /* fromHome */,
+ false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
+ } else {
+ // Fall through below to the non-thumbnail transition
+ useThumbnailTransition = false;
+ }
+ }
+
+ if (!useThumbnailTransition) {
+ // If there is no thumbnail transition, but is launching from home into recents, then
+ // use a quick home transition and do the animation from home
+ if (hasRecentTasks) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ String homeActivityPackage = ssp.getHomeActivityPackageName();
+ String searchWidgetPackage = null;
+ if (RecentsDebugFlags.Static.EnableSearchBar) {
+ searchWidgetPackage = Prefs.getString(mContext,
+ Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null);
+ } else {
+ AppWidgetProviderInfo searchWidgetInfo = ssp.resolveSearchAppWidget();
+ if (searchWidgetInfo != null) {
+ searchWidgetPackage = searchWidgetInfo.provider.getPackageName();
+ }
+ }
+
+ // Determine whether we are coming from a search owned home activity
+ boolean fromSearchHome = (homeActivityPackage != null) &&
+ homeActivityPackage.equals(searchWidgetPackage);
+ ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
+ startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
+ false /* fromThumbnail */, stackVr);
+ } else {
+ // Otherwise we do the normal fade from an unknown source
+ ActivityOptions opts = getUnknownTransitionActivityOptions();
+ startRecentsActivity(topTask, opts, true /* fromHome */,
+ false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
+ }
+ }
+ mLastToggleTime = SystemClock.elapsedRealtime();
+ }
+
+ /**
* Starts the recents activity.
*/
private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
index 48a1904..365b29d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
@@ -29,6 +29,7 @@
public class ViewFocusAnimator implements View.OnFocusChangeListener {
private final float mUnselectedScale;
+ private final float mSelectedScale;
private final float mSelectedScaleDelta;
private final float mUnselectedZ;
private final float mSelectedZDelta;
@@ -49,8 +50,9 @@
TypedValue out = new TypedValue();
res.getValue(R.integer.unselected_scale, out, true);
mUnselectedScale = out.getFloat();
- mSelectedScaleDelta = res.getFraction(R.fraction.lb_focus_zoom_factor_medium, 1, 1) -
- mUnselectedScale;
+ res.getValue(R.integer.selected_scale, out, true);
+ mSelectedScale = out.getFloat();
+ mSelectedScaleDelta = mSelectedScale - mUnselectedScale;
mUnselectedZ = res.getDimensionPixelOffset(R.dimen.recents_tv_unselected_item_z);
mSelectedZDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_selected_item_z_delta);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
index ef8d48e..bd3143f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
@@ -16,10 +16,8 @@
package com.android.systemui.recents.tv.views;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.content.Context;
-import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -121,10 +119,10 @@
};
}
try {
- Rect taskRect = taskView.getGlobalRect();
+ Rect taskRect = taskView.getFocusedThumbnailRect();
WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionThumb(task.thumbnail, taskRect.left,
- taskRect.top, callback, true);
+ .overridePendingAppTransitionAspectScaledThumb(task.thumbnail, taskRect.left,
+ taskRect.top, taskRect.width(), taskRect.height(), callback, true);
} catch (RemoteException e) {
Log.w(TAG, "Failed to override transition: " + e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index 7d8a3ce..5775b60 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -16,8 +16,13 @@
package com.android.systemui.recents.tv.views;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -71,15 +76,46 @@
mThumbnailView.getFocusedRect(r);
}
- public Rect getFocusedRect() {
+ public Rect getFocusedThumbnailRect() {
Rect r = new Rect();
- getFocusedRect(r);
+ mThumbnailView.getGlobalVisibleRect(r);
+ TypedValue out = new TypedValue();
+ getContext().getResources().getValue(R.integer.selected_scale, out, true);
+ float deltaScale = (out.getFloat() - 1.0f) / 2;
+ r.set((int) (r.left - r.left * deltaScale),
+ (int) (r.top - r.top * deltaScale),
+ (int) (r.right + r.right * deltaScale),
+ (int) (r.bottom + r.bottom * deltaScale));
return r;
}
- public Rect getGlobalRect() {
- Rect r = new Rect();
- getGlobalVisibleRect(r);
- return r;
+ public static Rect getStartingCardThumbnailRect(Context context) {
+ Resources res = context.getResources();
+
+ TypedValue out = new TypedValue();
+ res.getValue(R.integer.selected_scale, out, true);
+ float scale = out.getFloat();
+
+ int width = res.getDimensionPixelOffset(R.dimen.recents_tv_card_width);
+ int widthDelta = (int) (width * scale - width);
+ int height = (int) (res.getDimensionPixelOffset(
+ R.dimen.recents_tv_screenshot_height) * scale);
+ int padding = res.getDimensionPixelOffset(R.dimen.recents_tv_grid_row_padding);
+
+ int headerHeight = (int) ((res.getDimensionPixelOffset(
+ R.dimen.recents_tv_card_extra_badge_size) +
+ res.getDimensionPixelOffset(R.dimen.recents_tv_icon_padding_bottom)) * scale);
+
+ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = wm.getDefaultDisplay();
+ Point size = new Point();
+ display.getSize(size);
+ int screenWidth = size.x;
+ int screenHeight = size.y;
+
+ return new Rect(screenWidth - width - padding - widthDelta / 2,
+ screenHeight / 2 - height / 2 + headerHeight / 2,
+ screenWidth - padding + widthDelta / 2,
+ screenHeight / 2 + height / 2 + headerHeight / 2);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index a6c9f26..bb4a771 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -20,6 +20,7 @@
import android.animation.AnimatorListenerAdapter;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -31,6 +32,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -138,6 +140,8 @@
protected static final boolean ENABLE_HEADS_UP = true;
protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+ private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+
// Should match the values in PhoneWindowManager
public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
@@ -146,6 +150,8 @@
"com.android.systemui.statusbar.banner_action_cancel";
private static final String BANNER_ACTION_SETUP =
"com.android.systemui.statusbar.banner_action_setup";
+ private static final String WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION
+ = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
protected CommandQueue mCommandQueue;
protected IStatusBarService mBarService;
@@ -199,6 +205,9 @@
private UserManager mUserManager;
private int mDensity;
+ private KeyguardManager mKeyguardManager;
+ private LockPatternUtils mLockPatternUtils;
+
// UI-specific methods
/**
@@ -499,6 +508,20 @@
);
}
+ } else if (WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION.equals(action)) {
+ final IntentSender intentSender = (IntentSender) intent
+ .getParcelableExtra(Intent.EXTRA_INTENT);
+ final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
+ try {
+ mContext.startIntentSender(intentSender, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ /* ignore */
+ }
+ try {
+ mBarService.onNotificationClick(notificationKey);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
}
}
};
@@ -661,6 +684,8 @@
mDensity = currentConfig.densityDpi;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mLockPatternUtils = new LockPatternUtils(mContext);
// Connect in to the status bar manager service
mCommandQueue = new CommandQueue(this);
@@ -723,10 +748,14 @@
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_PRESENT);
- filter.addAction(BANNER_ACTION_CANCEL);
- filter.addAction(BANNER_ACTION_SETUP);
mContext.registerReceiver(mBroadcastReceiver, filter);
+ IntentFilter internalFilter = new IntentFilter();
+ internalFilter.addAction(WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
+ internalFilter.addAction(BANNER_ACTION_CANCEL);
+ internalFilter.addAction(BANNER_ACTION_SETUP);
+ mContext.registerReceiver(mBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
+
IntentFilter allUsersFilter = new IntentFilter();
allUsersFilter.addAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
@@ -739,8 +768,7 @@
if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
Log.d(TAG, "user hasn't seen notification about hidden notifications");
- final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
- if (!lockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
+ if (!mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
Log.d(TAG, "insecure lockscreen, skipping notification");
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
@@ -1079,6 +1107,7 @@
});
a.start();
guts.setExposed(true);
+ row.closeRemoteInput();
mStackScroller.onHeightChanged(null, true /* needsAnimation */);
mNotificationGutsExposed = guts;
}
@@ -1680,7 +1709,6 @@
ActivityManagerNative.getDefault().resumeAppSwitches();
} catch (RemoteException e) {
}
-
try {
intent.send();
} catch (PendingIntent.CanceledException e) {
@@ -1773,8 +1801,22 @@
ActivityManagerNative.getDefault().resumeAppSwitches();
} catch (RemoteException e) {
}
-
if (intent != null) {
+ // If we are launching a work activity and require to launch
+ // separate work challenge, we defer the activity action and cancel
+ // notification until work challenge is unlocked.
+ if (intent.isActivity()) {
+ final int userId = intent.getCreatorUserHandle()
+ .getIdentifier();
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+ && mKeyguardManager.isDeviceLocked(userId)) {
+ // Show work challenge, do not run pendingintent and
+ // remove notification
+ startWorkChallenge(userId, intent.getIntentSender(),
+ notificationKey);
+ return;
+ }
+ }
try {
intent.send();
} catch (PendingIntent.CanceledException e) {
@@ -1809,6 +1851,23 @@
}, afterKeyguardGone);
}
+ public void startWorkChallenge(int userId, IntentSender intendSender,
+ String notificationKey) {
+ final Intent callBackIntent = new Intent(
+ WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
+ callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
+ callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
+ callBackIntent.setPackage(mContext.getPackageName());
+
+ final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
+ null, userId);
+ newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ newIntent.putExtra(Intent.EXTRA_INTENT, PendingIntent
+ .getBroadcast(mContext, 0, callBackIntent, 0).getIntentSender());
+ mContext.startActivity(newIntent);
+ }
+
public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
if (notification.contentIntent != null || notification.fullScreenIntent != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 246f15e..22bb8eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -579,6 +579,11 @@
}
}
+ public void closeRemoteInput() {
+ mPrivateLayout.closeRemoteInput();
+ mPublicLayout.closeRemoteInput();
+ }
+
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 3b87577..c2df292 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -69,6 +69,9 @@
private View mHeadsUpChild;
private HybridNotificationView mSingleLineView;
+ private RemoteInputView mExpandedRemoteInput;
+ private RemoteInputView mHeadsUpRemoteInput;
+
private NotificationViewWrapper mContractedWrapper;
private NotificationViewWrapper mExpandedWrapper;
private NotificationViewWrapper mHeadsUpWrapper;
@@ -743,15 +746,19 @@
View bigContentView = mExpandedChild;
if (bigContentView != null) {
- applyRemoteInput(bigContentView, entry, hasRemoteInput);
+ mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput);
+ } else {
+ mExpandedRemoteInput = null;
}
View headsUpContentView = mHeadsUpChild;
if (headsUpContentView != null) {
- applyRemoteInput(headsUpContentView, entry, hasRemoteInput);
+ mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput);
+ } else {
+ mHeadsUpRemoteInput = null;
}
}
- private void applyRemoteInput(View view, NotificationData.Entry entry, boolean hasRemoteInput) {
+ private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry, boolean hasRemoteInput) {
View actionContainerCandidate = view.findViewById(
com.android.internal.R.id.actions_container);
if (actionContainerCandidate instanceof FrameLayout) {
@@ -777,7 +784,20 @@
color = mContext.getColor(R.color.default_remote_input_background);
}
riv.setBackgroundColor(color);
+
+ return riv;
}
+ return existing;
+ }
+ return null;
+ }
+
+ public void closeRemoteInput() {
+ if (mHeadsUpRemoteInput != null) {
+ mHeadsUpRemoteInput.close();
+ }
+ if (mExpandedRemoteInput != null) {
+ mExpandedRemoteInput.close();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index e53f044..99896f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -36,7 +36,6 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
-import android.view.Gravity;
import android.view.IDockedStackListener.Stub;
import android.view.MotionEvent;
import android.view.Surface;
@@ -46,7 +45,6 @@
import android.view.WindowManagerGlobal;
import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
-
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.stackdivider.Divider;
@@ -54,7 +52,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
public class NavigationBarView extends LinearLayout {
final static boolean DEBUG = false;
@@ -100,6 +97,7 @@
private boolean mDockedStackExists;
private final SparseArray<ButtonDispatcher> mButtonDisatchers = new SparseArray<>();
+ private int mDensity;
private class NavTransitionListener implements TransitionListener {
private boolean mBackTransitioning;
@@ -190,6 +188,7 @@
mShowMenu = false;
mGestureHelper = new NavigationBarGestureHelper(context);
+ mDensity = context.getResources().getConfiguration().densityDpi;
getIcons(context);
mBarTransitions = new NavigationBarTransitions(this);
@@ -599,6 +598,10 @@
// we are switching to.
setNavigationIconHints(mNavigationIconHints, true);
}
+ if (mDensity != newConfig.densityDpi) {
+ mDensity = newConfig.densityDpi;
+ getIcons(getContext());
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index eb5b57e..95f26d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -16,24 +16,42 @@
package com.android.systemui.statusbar.phone;
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
+import android.annotation.LayoutRes;
import android.app.StatusBarManager;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.media.session.MediaSessionLegacyHelper;
+import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.util.AttributeSet;
+import android.view.ActionMode;
+import android.view.InputQueue;
import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.SurfaceHolder;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.widget.FrameLayout;
+import com.android.internal.view.FloatingActionMode;
+import com.android.internal.widget.FloatingToolbar;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -57,6 +75,13 @@
private final Paint mTransparentSrcPaint = new Paint();
private FalsingManager mFalsingManager;
+ // Implements the floating action mode for TextView's Cut/Copy/Past menu. Normally provided by
+ // DecorView, but since this is a special window we have to roll our own.
+ private View mFloatingActionModeOriginatingView;
+ private ActionMode mFloatingActionMode;
+ private FloatingToolbar mFloatingToolbar;
+ private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
+
public StatusBarWindowView(Context context, AttributeSet attrs) {
super(context, attrs);
setMotionEventSplittingEnabled(false);
@@ -301,5 +326,341 @@
a.recycle();
}
}
+
+ @Override
+ public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback,
+ int type) {
+ if (type == ActionMode.TYPE_FLOATING) {
+ return startActionMode(originalView, callback, type);
+ }
+ return super.startActionModeForChild(originalView, callback, type);
+ }
+
+ private ActionMode createFloatingActionMode(
+ View originatingView, ActionMode.Callback2 callback) {
+ if (mFloatingActionMode != null) {
+ mFloatingActionMode.finish();
+ }
+ cleanupFloatingActionModeViews();
+ final FloatingActionMode mode =
+ new FloatingActionMode(mContext, callback, originatingView);
+ mFloatingActionModeOriginatingView = originatingView;
+ mFloatingToolbarPreDrawListener =
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ mode.updateViewLocationInWindow();
+ return true;
+ }
+ };
+ return mode;
+ }
+
+ private void setHandledFloatingActionMode(ActionMode mode) {
+ mFloatingActionMode = mode;
+ mFloatingToolbar = new FloatingToolbar(mContext, mFakeWindow);
+ ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar);
+ mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
+ mFloatingActionModeOriginatingView.getViewTreeObserver()
+ .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
+ }
+
+ private void cleanupFloatingActionModeViews() {
+ if (mFloatingToolbar != null) {
+ mFloatingToolbar.dismiss();
+ mFloatingToolbar = null;
+ }
+ if (mFloatingActionModeOriginatingView != null) {
+ if (mFloatingToolbarPreDrawListener != null) {
+ mFloatingActionModeOriginatingView.getViewTreeObserver()
+ .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
+ mFloatingToolbarPreDrawListener = null;
+ }
+ mFloatingActionModeOriginatingView = null;
+ }
+ }
+
+ private ActionMode startActionMode(
+ View originatingView, ActionMode.Callback callback, int type) {
+ ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
+ ActionMode mode = createFloatingActionMode(originatingView, wrappedCallback);
+ if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
+ setHandledFloatingActionMode(mode);
+ } else {
+ mode = null;
+ }
+ return mode;
+ }
+
+ private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
+ private final ActionMode.Callback mWrapped;
+
+ public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
+ mWrapped = wrapped;
+ }
+
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return mWrapped.onCreateActionMode(mode, menu);
+ }
+
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ requestFitSystemWindows();
+ return mWrapped.onPrepareActionMode(mode, menu);
+ }
+
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return mWrapped.onActionItemClicked(mode, item);
+ }
+
+ public void onDestroyActionMode(ActionMode mode) {
+ mWrapped.onDestroyActionMode(mode);
+ if (mode == mFloatingActionMode) {
+ cleanupFloatingActionModeViews();
+ mFloatingActionMode = null;
+ }
+ requestFitSystemWindows();
+ }
+
+ @Override
+ public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
+ if (mWrapped instanceof ActionMode.Callback2) {
+ ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
+ } else {
+ super.onGetContentRect(mode, view, outRect);
+ }
+ }
+ }
+
+ /**
+ * Minimal window to satisfy FloatingToolbar.
+ */
+ private Window mFakeWindow = new Window(mContext) {
+ @Override
+ public void takeSurface(SurfaceHolder.Callback2 callback) {
+ }
+
+ @Override
+ public void takeInputQueue(InputQueue.Callback callback) {
+ }
+
+ @Override
+ public boolean isFloating() {
+ return false;
+ }
+
+ @Override
+ public void alwaysReadCloseOnTouchAttr() {
+ }
+
+ @Override
+ public void setContentView(@LayoutRes int layoutResID) {
+ }
+
+ @Override
+ public void setContentView(View view) {
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ }
+
+ @Override
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ }
+
+ @Override
+ public void clearContentView() {
+ }
+
+ @Override
+ public View getCurrentFocus() {
+ return null;
+ }
+
+ @Override
+ public LayoutInflater getLayoutInflater() {
+ return null;
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ }
+
+ @Override
+ public void setTitleColor(@ColorInt int textColor) {
+ }
+
+ @Override
+ public void openPanel(int featureId, KeyEvent event) {
+ }
+
+ @Override
+ public void closePanel(int featureId) {
+ }
+
+ @Override
+ public void togglePanel(int featureId, KeyEvent event) {
+ }
+
+ @Override
+ public void invalidatePanelMenu(int featureId) {
+ }
+
+ @Override
+ public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
+ return false;
+ }
+
+ @Override
+ public boolean performPanelIdentifierAction(int featureId, int id, int flags) {
+ return false;
+ }
+
+ @Override
+ public void closeAllPanels() {
+ }
+
+ @Override
+ public boolean performContextMenuIdentifierAction(int id, int flags) {
+ return false;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ }
+
+ @Override
+ public void setBackgroundDrawable(Drawable drawable) {
+ }
+
+ @Override
+ public void setFeatureDrawableResource(int featureId, @DrawableRes int resId) {
+ }
+
+ @Override
+ public void setFeatureDrawableUri(int featureId, Uri uri) {
+ }
+
+ @Override
+ public void setFeatureDrawable(int featureId, Drawable drawable) {
+ }
+
+ @Override
+ public void setFeatureDrawableAlpha(int featureId, int alpha) {
+ }
+
+ @Override
+ public void setFeatureInt(int featureId, int value) {
+ }
+
+ @Override
+ public void takeKeyEvents(boolean get) {
+ }
+
+ @Override
+ public boolean superDispatchKeyEvent(KeyEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean superDispatchTouchEvent(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean superDispatchTrackballEvent(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean superDispatchGenericMotionEvent(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public View getDecorView() {
+ return StatusBarWindowView.this;
+ }
+
+ @Override
+ public View peekDecorView() {
+ return null;
+ }
+
+ @Override
+ public Bundle saveHierarchyState() {
+ return null;
+ }
+
+ @Override
+ public void restoreHierarchyState(Bundle savedInstanceState) {
+ }
+
+ @Override
+ protected void onActive() {
+ }
+
+ @Override
+ public void setChildDrawable(int featureId, Drawable drawable) {
+ }
+
+ @Override
+ public void setChildInt(int featureId, int value) {
+ }
+
+ @Override
+ public boolean isShortcutKey(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ @Override
+ public void setVolumeControlStream(int streamType) {
+ }
+
+ @Override
+ public int getVolumeControlStream() {
+ return 0;
+ }
+
+ @Override
+ public int getStatusBarColor() {
+ return 0;
+ }
+
+ @Override
+ public void setStatusBarColor(@ColorInt int color) {
+ }
+
+ @Override
+ public int getNavigationBarColor() {
+ return 0;
+ }
+
+ @Override
+ public void setNavigationBarColor(@ColorInt int color) {
+ }
+
+ @Override
+ public void setDecorCaptionShade(int decorCaptionShade) {
+ }
+
+ @Override
+ public void setResizingCaptionDrawable(Drawable drawable) {
+ }
+
+ @Override
+ public void onMultiWindowChanged() {
+ }
+
+ @Override
+ public void reportActivityRelaunched() {
+ }
+ };
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 29b0f4b..3a0336b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -29,8 +29,10 @@
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -44,6 +46,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.stack.LongPressCancelable;
/**
* Host for the remote input.
@@ -64,6 +67,7 @@
private RemoteInputController mController;
private NotificationData.Entry mEntry;
+ private LongPressCancelable mLongPressCancelable;
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -226,6 +230,30 @@
updateSendButton();
}
+ public void close() {
+ mEditText.defocusIfNeeded();
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ if (mLongPressCancelable == null) {
+ ViewParent p = getParent();
+ while (p != null) {
+ if (p instanceof LongPressCancelable) {
+ mLongPressCancelable = (LongPressCancelable) p;
+ break;
+ }
+ p = p.getParent();
+ }
+ }
+ if (mLongPressCancelable != null) {
+ mLongPressCancelable.requestDisallowLongPress();
+ }
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
/**
* An EditText that changes appearance based on whether it's focusable and becomes
* un-focusable whenever the user navigates away from it or it becomes invisible.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/LongPressCancelable.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/LongPressCancelable.java
new file mode 100644
index 0000000..05f0c07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/LongPressCancelable.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.stack;
+
+/**
+ * Interface for container layouts that listen for long presses. A child that
+ * wants to handle long press can use this to cancel the parents long press logic.
+ */
+public interface LongPressCancelable {
+ /**
+ * Request that the view does not perform long press for the current touch.
+ */
+ void requestDisallowLongPress();
+}
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 59ec6112..686a712 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -83,7 +83,7 @@
public class NotificationStackScrollLayout extends ViewGroup
implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
- SettingsIconRowListener {
+ SettingsIconRowListener, LongPressCancelable {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
@@ -2627,6 +2627,11 @@
}
}
+ @Override
+ public void requestDisallowLongPress() {
+ removeLongPressCallback();
+ }
+
public void removeLongPressCallback() {
mSwipeHelper.removeLongPressCallback();
}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index e7db2a8..a94c8b8 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -447,8 +447,12 @@
int[] ops) {
mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return Collections.emptyList();
+ }
synchronized (this) {
- Ops pkgOps = getOpsLocked(uid, packageName, false);
+ Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false);
if (pkgOps == null) {
return null;
}
@@ -466,7 +470,7 @@
private void pruneOp(Op op, int uid, String packageName) {
if (op.time == 0 && op.rejectTime == 0) {
- Ops ops = getOpsLocked(uid, packageName, false);
+ Ops ops = getOpsRawLocked(uid, packageName, false);
if (ops != null) {
ops.remove(op.op);
if (ops.size() <= 0) {
@@ -880,8 +884,12 @@
public int checkOperation(int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
synchronized (this) {
- if (isOpRestricted(uid, code, packageName)) {
+ if (isOpRestricted(uid, code, resolvedPackageName)) {
return AppOpsManager.MODE_IGNORED;
}
code = AppOpsManager.opToSwitch(code);
@@ -892,7 +900,7 @@
return uidMode;
}
}
- Op op = getOpLocked(code, uid, packageName, false);
+ Op op = getOpLocked(code, uid, resolvedPackageName, false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
@@ -968,6 +976,7 @@
@Override
public int checkPackage(int uid, String packageName) {
+ Preconditions.checkNotNull(packageName);
synchronized (this) {
if (getOpsRawLocked(uid, packageName, true) != null) {
return AppOpsManager.MODE_ALLOWED;
@@ -981,26 +990,39 @@
public int noteProxyOperation(int code, String proxyPackageName,
int proxiedUid, String proxiedPackageName) {
verifyIncomingOp(code);
- final int proxyMode = noteOperationUnchecked(code, Binder.getCallingUid(),
- proxyPackageName, -1, null);
+ final int proxyUid = Binder.getCallingUid();
+ String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
+ if (resolveProxyPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ final int proxyMode = noteOperationUnchecked(code, proxyUid,
+ resolveProxyPackageName, -1, null);
if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
return proxyMode;
}
- return noteOperationUnchecked(code, proxiedUid, proxiedPackageName,
- Binder.getCallingUid(), proxyPackageName);
+ String resolveProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
+ if (resolveProxiedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
+ proxyMode, resolveProxyPackageName);
}
@Override
public int noteOperation(int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
- return noteOperationUnchecked(code, uid, packageName, 0, null);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null);
}
private int noteOperationUnchecked(int code, int uid, String packageName,
int proxyUid, String proxyPackageName) {
synchronized (this) {
- Ops ops = getOpsLocked(uid, packageName, true);
+ Ops ops = getOpsRawLocked(uid, packageName, true);
if (ops == null) {
if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
@@ -1048,16 +1070,20 @@
public int startOperation(IBinder token, int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
ClientState client = (ClientState)token;
synchronized (this) {
- Ops ops = getOpsLocked(uid, packageName, true);
+ Ops ops = getOpsRawLocked(uid, resolvedPackageName, true);
if (ops == null) {
if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
- + " package " + packageName);
+ + " package " + resolvedPackageName);
return AppOpsManager.MODE_ERRORED;
}
Op op = getOpLocked(ops, code, true);
- if (isOpRestricted(uid, code, packageName)) {
+ if (isOpRestricted(uid, code, resolvedPackageName)) {
return AppOpsManager.MODE_IGNORED;
}
final int switchCode = AppOpsManager.opToSwitch(code);
@@ -1067,7 +1093,7 @@
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
- + packageName);
+ + resolvedPackageName);
op.rejectTime = System.currentTimeMillis();
return uidMode;
}
@@ -1075,12 +1101,13 @@
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
- + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
+ + switchCode + " (" + code + ") uid " + uid + " package "
+ + resolvedPackageName);
op.rejectTime = System.currentTimeMillis();
return switchOp.mode;
}
if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
- + " package " + packageName);
+ + " package " + resolvedPackageName);
if (op.nesting == 0) {
op.time = System.currentTimeMillis();
op.rejectTime = 0;
@@ -1098,9 +1125,16 @@
public void finishOperation(IBinder token, int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
- ClientState client = (ClientState)token;
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return;
+ }
+ if (!(token instanceof ClientState)) {
+ return;
+ }
+ ClientState client = (ClientState) token;
synchronized (this) {
- Op op = getOpLocked(code, uid, packageName, true);
+ Op op = getOpLocked(code, uid, resolvedPackageName, true);
if (op == null) {
return;
}
@@ -1116,6 +1150,9 @@
@Override
public int permissionToOpCode(String permission) {
+ if (permission == null) {
+ return AppOpsManager.OP_NONE;
+ }
return AppOpsManager.permissionToOpCode(permission);
}
@@ -1165,15 +1202,6 @@
return uidState;
}
- private Ops getOpsLocked(int uid, String packageName, boolean edit) {
- if (uid == 0) {
- packageName = "root";
- } else if (uid == Process.SHELL_UID) {
- packageName = "com.android.shell";
- }
- return getOpsRawLocked(uid, packageName, edit);
- }
-
private Ops getOpsRawLocked(int uid, String packageName, boolean edit) {
UidState uidState = getUidStateLocked(uid, edit);
if (uidState == null) {
@@ -1259,7 +1287,7 @@
}
private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
- Ops ops = getOpsLocked(uid, packageName, edit);
+ Ops ops = getOpsRawLocked(uid, packageName, edit);
if (ops == null) {
return null;
}
@@ -1317,7 +1345,7 @@
if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
// If we are the system, bypass user restrictions for certain codes
synchronized (this) {
- Ops ops = getOpsLocked(uid, packageName, true);
+ Ops ops = getOpsRawLocked(uid, packageName, true);
if ((ops != null) && ops.isPrivileged) {
return false;
}
@@ -1582,7 +1610,7 @@
out.startTag(null, "uid");
out.attribute(null, "n", Integer.toString(pkg.getUid()));
synchronized (this) {
- Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false);
+ Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), false);
// Should always be present as the list of PackageOps is generated
// from Ops.
if (ops != null) {
@@ -2103,6 +2131,7 @@
@Override
public void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle) {
checkSystemUid("setUserRestrictions");
+ Preconditions.checkNotNull(restrictions);
Preconditions.checkNotNull(token);
final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle);
for (int i = 0; i < opRestrictions.length; ++i) {
@@ -2317,6 +2346,15 @@
}
}
+ private static String resolvePackageName(int uid, String packageName) {
+ if (uid == 0) {
+ return "root";
+ } else if (uid == Process.SHELL_UID) {
+ return "com.android.shell";
+ }
+ return packageName;
+ }
+
private static String[] getPackagesForUid(int uid) {
String[] packageNames = null;
try {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index cb3aeae..322df04 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -597,7 +597,7 @@
if (sharedAccounts == null || sharedAccounts.length == 0) return;
Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
int parentUserId = UserManager.isSplitSystemUser()
- ? mUserManager.getUserInfo(userId).restrictedProfileParentId
+ ? getUserManager().getUserInfo(userId).restrictedProfileParentId
: UserHandle.USER_SYSTEM;
if (parentUserId < 0) {
Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
@@ -1061,7 +1061,7 @@
for (UserInfo user : users) {
if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
addSharedAccountAsUser(account, user.id);
- if (mUserManager.isUserUnlocked(user.id)) {
+ if (getUserManager().isUserUnlocked(user.id)) {
mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
}
@@ -1270,7 +1270,7 @@
* Owner or system user account was renamed, rename the account for
* those users with which the account was shared.
*/
- List<UserInfo> users = mUserManager.getUsers(true);
+ List<UserInfo> users = getUserManager().getUsers(true);
for (UserInfo user : users) {
if (user.isRestricted()
&& (user.restrictedProfileParentId == parentUserId)) {
@@ -1286,7 +1286,7 @@
}
private boolean canHaveProfile(final int parentUserId) {
- final UserInfo userInfo = mUserManager.getUserInfo(parentUserId);
+ final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
return userInfo != null && userInfo.canHaveProfile();
}
@@ -1477,7 +1477,7 @@
int parentUserId = accounts.userId;
if (canHaveProfile(parentUserId)) {
// Remove from any restricted profiles that are sharing this account.
- List<UserInfo> users = mUserManager.getUsers(true);
+ List<UserInfo> users = getUserManager().getUsers(true);
for (UserInfo user : users) {
if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
removeSharedAccountAsUser(account, user.id, callingUid);
@@ -4730,7 +4730,7 @@
|| callingUid == Process.myUid()) {
return unfiltered;
}
- UserInfo user = mUserManager.getUserInfo(userAccounts.userId);
+ UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
if (user != null && user.isRestricted()) {
String[] packages = mPackageManager.getPackagesForUid(callingUid);
// If any of the packages is a white listed package, return the full set,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5320221..b4df689 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1290,7 +1290,7 @@
// Check if the ringer mode handles this adjustment. If it does we don't
// need to adjust the volume further.
final int result = checkForRingerModeChange(aliasIndex, direction, step,
- streamState.mIsMuted, caller);
+ streamState.mIsMuted);
adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
// If suppressing a volume adjustment in silent mode, display the UI hint
if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
@@ -3361,8 +3361,7 @@
* adjusting volume. If so, this will set the proper ringer mode and volume
* indices on the stream states.
*/
- private int checkForRingerModeChange(int oldIndex, int direction, int step, boolean isMuted,
- String caller) {
+ private int checkForRingerModeChange(int oldIndex, int direction, int step, boolean isMuted) {
final boolean isTv = mPlatformType == AudioSystem.PLATFORM_TELEVISION;
int result = FLAG_ADJUST_VOLUME;
int ringerMode = getRingerModeInternal();
@@ -3451,11 +3450,6 @@
break;
}
- if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode)
- && !mNm.isNotificationPolicyAccessGrantedForPackage(caller)) {
- throw new SecurityException("Not allowed to change Do Not Disturb state");
- }
-
setRingerMode(ringerMode, TAG + ".checkForRingerModeChange", false /*external*/);
mPrevVolDirection = direction;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 075008a..9fd7f90 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -311,10 +311,10 @@
}
toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
- }
- startTrackingJob(jobStatus, toCancel);
- if (toCancel != null) {
- cancelJobImpl(toCancel);
+ if (toCancel != null) {
+ cancelJobImpl(toCancel);
+ }
+ startTrackingJob(jobStatus, toCancel);
}
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
return JobScheduler.RESULT_SUCCESS;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e8a6528..575de16 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3552,6 +3552,9 @@
public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
+ " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)));
+ if (mRankerServicePackageName == null) {
+ return;
+ }
if (pkgList != null && (pkgList.length > 0)) {
for (String pkgName : pkgList) {
@@ -3564,6 +3567,10 @@
protected void registerRanker() {
// Find the updatable ranker and register it.
+ if (mRankerServicePackageName == null) {
+ Slog.w(TAG, "could not start ranker service: no package specified!");
+ return;
+ }
Set<ComponentName> rankerComponents = queryPackageForServices(
mRankerServicePackageName, UserHandle.USER_SYSTEM, null);
Iterator<ComponentName> iterator = rankerComponents.iterator();
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 82e8799..39aa455 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -159,6 +159,8 @@
static final String TAG_EXTRAS = "extras";
static final String TAG_SHORTCUT = "shortcut";
static final String TAG_LAUNCHER = "launcher";
+ static final String TAG_PIN = "pin";
+ static final String TAG_LAUNCHER_PINS = "launcher-pins";
static final String ATTR_VALUE = "value";
static final String ATTR_NAME = "name";
@@ -174,6 +176,7 @@
static final String ATTR_FLAGS = "flags";
static final String ATTR_ICON_RES = "icon-res";
static final String ATTR_BITMAP_PATH = "bitmap-path";
+ static final String ATTR_PACKAGE_NAME = "package-name";
@VisibleForTesting
interface ConfigConstants {
@@ -723,7 +726,7 @@
/** Return the per-user state. */
@GuardedBy("mLock")
@NonNull
- private UserShortcuts getUserShortcutsLocked(@UserIdInt int userId) {
+ UserShortcuts getUserShortcutsLocked(@UserIdInt int userId) {
UserShortcuts userPackages = mUsers.get(userId);
if (userPackages == null) {
userPackages = loadUserLocked(userId);
@@ -738,15 +741,16 @@
/** Return the per-user per-package state. */
@GuardedBy("mLock")
@NonNull
- private PackageShortcuts getPackageShortcutsLocked(
+ PackageShortcuts getPackageShortcutsLocked(
@NonNull String packageName, @UserIdInt int userId) {
- final UserShortcuts userPackages = getUserShortcutsLocked(userId);
- PackageShortcuts shortcuts = userPackages.getPackages().get(packageName);
- if (shortcuts == null) {
- shortcuts = new PackageShortcuts(userId, packageName);
- userPackages.getPackages().put(packageName, shortcuts);
- }
- return shortcuts;
+ return getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ LauncherShortcuts getLauncherShortcuts(
+ @NonNull String packageName, @UserIdInt int userId) {
+ return getUserShortcutsLocked(userId).getLauncherShortcuts(packageName);
}
// === Caller validation ===
@@ -1056,7 +1060,7 @@
validatePersistableBundleForXml(shortcut.getIntentPersistableExtras());
validatePersistableBundleForXml(shortcut.getExtras());
- shortcut.setFlags(0);
+ shortcut.replaceFlags(0);
}
// KXmlSerializer is strict and doesn't allow certain characters, so we disallow those
@@ -1128,8 +1132,7 @@
// Then, add/update all. We need to make sure to take over "pinned" flag.
for (int i = 0; i < size; i++) {
final ShortcutInfo newShortcut = newShortcuts.get(i);
- newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
- ps.updateShortcutWithCapping(this, newShortcut);
+ ps.addDynamicShortcut(this, newShortcut);
}
}
userPackageChanged(packageName, userId);
@@ -1193,8 +1196,7 @@
fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
// Add it.
- newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
- ps.updateShortcutWithCapping(this, newShortcut);
+ ps.addDynamicShortcut(this, newShortcut);
}
userPackageChanged(packageName, userId);
@@ -1250,7 +1252,8 @@
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
- getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags);
+ getPackageShortcutsLocked(packageName, userId).findAll(this, ret, query, cloneFlags,
+ /* callingLauncher= */ null);
return new ParceledListSlice<>(ret);
}
@@ -1405,25 +1408,27 @@
synchronized (mLock) {
if (packageName != null) {
- getShortcutsInnerLocked(packageName, changedSince, componentName, queryFlags,
- userId, ret, cloneFlag);
+ getShortcutsInnerLocked(
+ callingPackage, packageName, changedSince,
+ componentName, queryFlags, userId, ret, cloneFlag);
} else {
final ArrayMap<String, PackageShortcuts> packages =
getUserShortcutsLocked(userId).getPackages();
for (int i = packages.size() - 1; i >= 0; i--) {
getShortcutsInnerLocked(
- packages.keyAt(i),
- changedSince, componentName, queryFlags, userId, ret, cloneFlag);
+ callingPackage, packages.keyAt(i), changedSince,
+ componentName, queryFlags, userId, ret, cloneFlag);
}
}
}
return ret;
}
- private void getShortcutsInnerLocked(@Nullable String packageName,long changedSince,
+ private void getShortcutsInnerLocked(@NonNull String callingPackage,
+ @Nullable String packageName,long changedSince,
@Nullable ComponentName componentName, int queryFlags,
int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
- getPackageShortcutsLocked(packageName, userId).findAll(ret,
+ getPackageShortcutsLocked(packageName, userId).findAll(ShortcutService.this, ret,
(ShortcutInfo si) -> {
if (si.getLastChangedTimestamp() < changedSince) {
return false;
@@ -1434,12 +1439,12 @@
}
final boolean matchDynamic =
((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
- && si.isDynamic();
+ && si.isDynamic();
final boolean matchPinned =
((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
&& si.isPinned();
return matchDynamic || matchPinned;
- }, cloneFlag);
+ }, cloneFlag, callingPackage);
}
@Override
@@ -1452,9 +1457,10 @@
final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
final ArraySet<String> idSet = new ArraySet<>(ids);
synchronized (mLock) {
- getPackageShortcutsLocked(packageName, userId).findAll(ret,
+ getPackageShortcutsLocked(packageName, userId).findAll(
+ ShortcutService.this, ret,
(ShortcutInfo si) -> idSet.contains(si.getId()),
- ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+ ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage);
}
return ret;
}
@@ -1467,8 +1473,8 @@
Preconditions.checkNotNull(shortcutIds, "shortcutIds");
synchronized (mLock) {
- getPackageShortcutsLocked(packageName, userId).replacePinned(
- ShortcutService.this, callingPackage, shortcutIds);
+ getLauncherShortcuts(callingPackage, userId).pinShortcuts(
+ ShortcutService.this, packageName, shortcutIds);
}
userPackageChanged(packageName, userId);
}
@@ -1811,6 +1817,14 @@
Binder.restoreCallingIdentity(token);
}
+ final void wtf(String message) {
+ Slog.wtf(TAG, message, /* exception= */ null);
+ }
+
+ void wtf(String message, Exception e) {
+ Slog.wtf(TAG, message, e);
+ }
+
File injectSystemDataPath() {
return Environment.getDataSystemDirectory();
}
@@ -1886,6 +1900,8 @@
private final ArrayMap<String, PackageShortcuts> mPackages = new ArrayMap<>();
+ private final ArrayMap<String, LauncherShortcuts> mLaunchers = new ArrayMap<>();
+
private ComponentName mLauncherComponent;
public UserShortcuts(int userId) {
@@ -1896,13 +1912,41 @@
return mPackages;
}
+ public ArrayMap<String, LauncherShortcuts> getLaunchers() {
+ return mLaunchers;
+ }
+
+ public PackageShortcuts getPackageShortcuts(@NonNull String packageName) {
+ PackageShortcuts ret = mPackages.get(packageName);
+ if (ret == null) {
+ ret = new PackageShortcuts(mUserId, packageName);
+ mPackages.put(packageName, ret);
+ }
+ return ret;
+ }
+
+ public LauncherShortcuts getLauncherShortcuts(@NonNull String packageName) {
+ LauncherShortcuts ret = mLaunchers.get(packageName);
+ if (ret == null) {
+ ret = new LauncherShortcuts(mUserId, packageName);
+ mLaunchers.put(packageName, ret);
+ }
+ return ret;
+ }
+
public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
out.startTag(null, ShortcutService.TAG_USER);
ShortcutService.writeTagValue(out, ShortcutService.TAG_LAUNCHER,
mLauncherComponent);
- for (int i = 0; i < mPackages.size(); i++) {
+ final int lsize = mLaunchers.size();
+ for (int i = 0; i < lsize; i++) {
+ mLaunchers.valueAt(i).saveToXml(out);
+ }
+
+ final int psize = mPackages.size();
+ for (int i = 0; i < psize; i++) {
mPackages.valueAt(i).saveToXml(out);
}
@@ -1923,16 +1967,26 @@
final int depth = parser.getDepth();
final String tag = parser.getName();
switch (tag) {
- case ShortcutService.TAG_LAUNCHER:
+ case ShortcutService.TAG_LAUNCHER: {
ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
parser, ShortcutService.ATTR_VALUE);
continue;
- case ShortcutService.TAG_PACKAGE:
+ }
+ case ShortcutService.TAG_PACKAGE: {
final PackageShortcuts shortcuts = PackageShortcuts.loadFromXml(parser, userId);
// Don't use addShortcut(), we don't need to save the icon.
ret.getPackages().put(shortcuts.mPackageName, shortcuts);
continue;
+ }
+
+ case ShortcutService.TAG_LAUNCHER_PINS: {
+ final LauncherShortcuts shortcuts =
+ LauncherShortcuts.loadFromXml(parser, userId);
+
+ ret.getLaunchers().put(shortcuts.mPackageName, shortcuts);
+ continue;
+ }
}
throw ShortcutService.throwForInvalidTag(depth, tag);
}
@@ -1969,12 +2023,166 @@
pw.print(mLauncherComponent);
pw.println();
+ for (int i = 0; i < mLaunchers.size(); i++) {
+ mLaunchers.valueAt(i).dump(s, pw, prefix + " ");
+ }
+
for (int i = 0; i < mPackages.size(); i++) {
mPackages.valueAt(i).dump(s, pw, prefix + " ");
}
}
}
+class LauncherShortcuts {
+ private static final String TAG = ShortcutService.TAG;
+
+ @UserIdInt
+ final int mUserId;
+
+ @NonNull
+ final String mPackageName;
+
+ /**
+ * Package name -> IDs.
+ */
+ final private ArrayMap<String, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
+
+ LauncherShortcuts(@UserIdInt int userId, @NonNull String packageName) {
+ mUserId = userId;
+ mPackageName = packageName;
+ }
+
+ public void pinShortcuts(@NonNull ShortcutService s, @NonNull String packageName,
+ @NonNull List<String> ids) {
+ final int idSize = ids.size();
+ if (idSize == 0) {
+ mPinnedShortcuts.remove(packageName);
+ } else {
+ final ArraySet<String> prevSet = mPinnedShortcuts.get(packageName);
+
+ // Pin shortcuts. Make sure only pin the ones that were visible to the caller.
+ // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here.
+
+ final PackageShortcuts packageShortcuts =
+ s.getPackageShortcutsLocked(packageName, mUserId);
+ final ArraySet<String> newSet = new ArraySet<>();
+
+ for (int i = 0; i < idSize; i++) {
+ final String id = ids.get(i);
+ final ShortcutInfo si = packageShortcuts.findShortcutById(id);
+ if (si == null) {
+ continue;
+ }
+ if (si.isDynamic() || (prevSet != null && prevSet.contains(id))) {
+ newSet.add(id);
+ }
+ }
+ mPinnedShortcuts.put(packageName, newSet);
+ }
+ s.getPackageShortcutsLocked(packageName, mUserId).refreshPinnedFlags(s);
+ }
+
+ /**
+ * Return the pinned shortcut IDs for the publisher package.
+ */
+ public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName) {
+ return mPinnedShortcuts.get(packageName);
+ }
+
+ /**
+ * Persist.
+ */
+ public void saveToXml(XmlSerializer out) throws IOException {
+ out.startTag(null, ShortcutService.TAG_LAUNCHER_PINS);
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_PACKAGE_NAME,
+ mPackageName);
+
+ final int size = mPinnedShortcuts.size();
+ for (int i = 0; i < size; i++) {
+ out.startTag(null, ShortcutService.TAG_PACKAGE);
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_PACKAGE_NAME,
+ mPinnedShortcuts.keyAt(i));
+
+ final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
+ final int idSize = ids.size();
+ for (int j = 0; j < idSize; j++) {
+ ShortcutService.writeTagValue(out, ShortcutService.TAG_PIN, ids.valueAt(j));
+ }
+ out.endTag(null, ShortcutService.TAG_PACKAGE);
+ }
+
+ out.endTag(null, ShortcutService.TAG_LAUNCHER_PINS);
+ }
+
+ /**
+ * Load.
+ */
+ public static LauncherShortcuts loadFromXml(XmlPullParser parser, int userId)
+ throws IOException, XmlPullParserException {
+ final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
+ ShortcutService.ATTR_PACKAGE_NAME);
+
+ final LauncherShortcuts ret = new LauncherShortcuts(userId, launcherPackageName);
+
+ ArraySet<String> ids = null;
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+ final String tag = parser.getName();
+ switch (tag) {
+ case ShortcutService.TAG_PACKAGE: {
+ final String packageName = ShortcutService.parseStringAttribute(parser,
+ ShortcutService.ATTR_PACKAGE_NAME);
+ ids = new ArraySet<>();
+ ret.mPinnedShortcuts.put(packageName, ids);
+ continue;
+ }
+ case ShortcutService.TAG_PIN: {
+ ids.add(ShortcutService.parseStringAttribute(parser,
+ ShortcutService.ATTR_VALUE));
+ continue;
+ }
+ }
+ throw ShortcutService.throwForInvalidTag(depth, tag);
+ }
+ return ret;
+ }
+
+ public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println();
+
+ pw.print(prefix);
+ pw.print("Launcher: ");
+ pw.print(mPackageName);
+ pw.println();
+
+ final int size = mPinnedShortcuts.size();
+ for (int i = 0; i < size; i++) {
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("Package: ");
+ pw.println(mPinnedShortcuts.keyAt(i));
+
+ final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
+ final int idSize = ids.size();
+
+ for (int j = 0; j < idSize; j++) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print(ids.valueAt(j));
+ pw.println();
+ }
+ }
+ }
+}
+
/**
* All the information relevant to shortcuts from a single package (per-user).
*/
@@ -2038,27 +2246,34 @@
*
* It checks the max number of dynamic shortcuts.
*/
- public void updateShortcutWithCapping(@NonNull ShortcutService s,
+ public void addDynamicShortcut(@NonNull ShortcutService s,
@NonNull ShortcutInfo newShortcut) {
+ newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
+
final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
- int oldFlags = 0;
- int newDynamicCount = mDynamicShortcutCount;
+ final boolean wasPinned;
+ final int newDynamicCount;
- if (oldShortcut != null) {
- oldFlags = oldShortcut.getFlags();
+ if (oldShortcut == null) {
+ wasPinned = false;
+ newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
+ } else {
+ wasPinned = oldShortcut.isPinned();
if (oldShortcut.isDynamic()) {
- newDynamicCount--;
+ newDynamicCount = mDynamicShortcutCount; // not adding a dynamic shortcut.
+ } else {
+ newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
}
}
- if (newShortcut.isDynamic()) {
- newDynamicCount++;
- }
+
// Make sure there's still room.
s.enforceMaxDynamicShortcuts(newDynamicCount);
// Okay, make it dynamic and add.
- newShortcut.addFlags(oldFlags);
+ if (wasPinned) {
+ newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
+ }
addShortcut(s, newShortcut);
mDynamicShortcutCount = newDynamicCount;
@@ -2087,6 +2302,9 @@
}
}
+ /**
+ * Remove all dynamic shortcuts.
+ */
public void deleteAllDynamicShortcuts(@NonNull ShortcutService s) {
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC);
@@ -2095,6 +2313,9 @@
mDynamicShortcutCount = 0;
}
+ /**
+ * Remove a dynamic shortcut by ID.
+ */
public void deleteDynamicWithId(@NonNull ShortcutService s, @NonNull String shortcutId) {
final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
@@ -2111,24 +2332,40 @@
}
}
- public void replacePinned(@NonNull ShortcutService s, String launcherPackage,
- List<String> shortcutIds) {
-
- // TODO Should be per launcherPackage.
-
+ /**
+ * Called after a launcher updates the pinned set. For each shortcut in this package,
+ * set FLAG_PINNED if any launcher has pinned it. Otherwise, clear it.
+ *
+ * <p>Then remove all shortcuts that are not dynamic and no longer pinned either.
+ */
+ public void refreshPinnedFlags(@NonNull ShortcutService s) {
// First, un-pin all shortcuts
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
}
- // Then pin ALL
- for (int i = shortcutIds.size() - 1; i >= 0; i--) {
- final ShortcutInfo shortcut = mShortcuts.get(shortcutIds.get(i));
- if (shortcut != null) {
- shortcut.addFlags(ShortcutInfo.FLAG_PINNED);
+ // Then, for the pinned set for each launcher, set the pin flag one by one.
+ final ArrayMap<String, LauncherShortcuts> launchers =
+ s.getUserShortcutsLocked(mUserId).getLaunchers();
+
+ for (int l = launchers.size() - 1; l >= 0; l--) {
+ final LauncherShortcuts launcherShortcuts = launchers.valueAt(l);
+ final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(mPackageName);
+
+ if (pinned == null || pinned.size() == 0) {
+ continue;
+ }
+ for (int i = pinned.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.get(pinned.valueAt(i));
+ if (si == null) {
+ s.wtf("Shortcut not found");
+ } else {
+ si.addFlags(ShortcutInfo.FLAG_PINNED);
+ }
}
}
+ // Lastly, remove the ones that are no longer pinned nor dynamic.
removeOrphans(s);
}
@@ -2172,12 +2409,40 @@
/**
* Find all shortcuts that match {@code query}.
*/
- public void findAll(@NonNull List<ShortcutInfo> result,
- @Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
+ public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
+ @Nullable Predicate<ShortcutInfo> query, int cloneFlag,
+ @Nullable String callingLauncher) {
+
+ // Set of pinned shortcuts by the calling launcher.
+ final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
+ : s.getLauncherShortcuts(callingLauncher, mUserId)
+ .getPinnedShortcutIds(mPackageName);
+
for (int i = 0; i < mShortcuts.size(); i++) {
final ShortcutInfo si = mShortcuts.valueAt(i);
- if (query == null || query.test(si)) {
- result.add(si.clone(cloneFlag));
+
+ // If it's called by non-launcher (i.e. publisher, always include -> true.
+ // Otherwise, only include non-dynamic pinned one, if the calling launcher has pinned
+ // it.
+ final boolean isPinnedByCaller = (callingLauncher == null)
+ || ((pinnedByCallerSet != null) && pinnedByCallerSet.contains(si.getId()));
+ if (!si.isDynamic()) {
+ if (!si.isPinned()) {
+ s.wtf("Shortcut not pinned here");
+ continue;
+ }
+ if (!isPinnedByCaller) {
+ continue;
+ }
+ }
+ final ShortcutInfo clone = si.clone(cloneFlag);
+ // Fix up isPinned for the caller. Note we need to do it before the "test" callback,
+ // since it may check isPinned.
+ if (!isPinnedByCaller) {
+ clone.clearFlags(ShortcutInfo.FLAG_PINNED);
+ }
+ if (query == null || query.test(clone)) {
+ result.add(clone);
}
}
}
@@ -2187,6 +2452,8 @@
}
public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println();
+
pw.print(prefix);
pw.print("Package: ");
pw.print(mPackageName);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index 6bcaf4d..5c51139 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -57,6 +57,7 @@
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
+import android.util.SparseArray;
import com.android.frameworks.servicestests.R;
import com.android.internal.util.Preconditions;
@@ -250,6 +251,12 @@
r.run();
mContext.injectRestoreCallingIdentity(token);
}
+
+ @Override
+ void wtf(String message, Exception e) {
+ // During tests, WTF is fatal.
+ fail(message + " exception: " + e);
+ }
}
/** ShortcutManager with injection override methods. */
@@ -1752,68 +1759,320 @@
public void testPinShortcutAndGetPinnedShortcuts() {
// Create some shortcuts.
- setCaller(CALLING_PACKAGE_1);
- final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
- final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
+ final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+ });
- setCaller(CALLING_PACKAGE_2);
- final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
- final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
- final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
+ final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
+ final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
+ });
- setCaller(CALLING_PACKAGE_3);
- final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s2", 1000);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2)));
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s2", 1000);
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s3_2)));
+ });
// Pin some.
- setCaller(LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ Arrays.asList("s2", "s3"), getCallingUser());
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
- Arrays.asList("s2", "s3"), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ Arrays.asList("s3", "s4", "s5"), getCallingUser());
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
- Arrays.asList("s3", "s4", "s5"), getCallingUser());
-
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_3,
- Arrays.asList("s3"), getCallingUser()); // Note ID doesn't exist
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3,
+ Arrays.asList("s3"), getCallingUser()); // Note ID doesn't exist
+ });
// Delete some.
- setCaller(CALLING_PACKAGE_1);
- assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
- mManager.deleteDynamicShortcut("s2");
- assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+ mManager.deleteDynamicShortcut("s2");
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+ });
- setCaller(CALLING_PACKAGE_2);
- assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
- mManager.deleteDynamicShortcut("s3");
- assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+ mManager.deleteDynamicShortcut("s3");
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+ });
- setCaller(CALLING_PACKAGE_3);
- assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
- mManager.deleteDynamicShortcut("s2");
- assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+ mManager.deleteDynamicShortcut("s2");
+ assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+ });
// Get pinned shortcuts from launcher
- setCaller(LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ // CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s2");
- // CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
- assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
- mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
- /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
- "s2");
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3", "s4");
- assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
- mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
- /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
- "s3", "s4");
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_3,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
+ /* none */);
+ });
+ }
- assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
- mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_3,
- /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
- /* none */);
+ public void testPinShortcutAndGetPinnedShortcuts_multi() {
+ // Create some shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ dumpsysOnLogcat();
+
+ // Pin some.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ Arrays.asList("s3", "s4"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ Arrays.asList("s1", "s2", "s4"), getCallingUser());
+ });
+
+ dumpsysOnLogcat();
+
+ // Delete some.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3");
+ mManager.deleteDynamicShortcut("s3");
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3");
+ });
+
+ dumpsysOnLogcat();
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s1", "s2");
+ mManager.deleteDynamicShortcut("s1");
+ mManager.deleteDynamicShortcut("s3");
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s1", "s2");
+ });
+
+ dumpsysOnLogcat();
+
+ // Get pinned shortcuts from launcher
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3");
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s1", "s2");
+
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s1", "s2", "s3");
+
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s1", "s2");
+ });
+
+ dumpsysOnLogcat();
+
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ // Launcher2 still has no pinned ones.
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
+ /* none */);
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
+ /* none */);
+
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s1", "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s2");
+
+ // Now pin some.
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ Arrays.asList("s1", "s2"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ Arrays.asList("s1", "s2"), getCallingUser());
+
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s1", "s2");
+
+ // S1 was not visible to it, so shouldn't be pinned.
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s2");
+ });
+
+ // Re-initialize and load from the files.
+ initService();
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3");
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s1", "s2");
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s1", "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
+ | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
+ "s2");
+ });
+
+ // Delete all dynamic.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.deleteAllDynamicShortcuts();
+
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2", "s3");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ mManager.deleteAllDynamicShortcuts();
+
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s2", "s1");
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3");
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s1", "s2");
+
+ // from all packages.
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, null,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s1", "s2", "s3");
+
+ // Update pined. Note s2 and s3 are actually available, but not visible to this
+ // launcher, so still can't be pinned.
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s1", "s2", "s3", "s4"),
+ getCallingUser());
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3");
+ });
+ // Re-publish s1.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), "s1");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2", "s3");
+ });
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3");
+
+ // Now "s1" is visible, so can be pinned.
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s1", "s2", "s3", "s4"),
+ getCallingUser());
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s1", "s3");
+ });
+
+ // Now clear pinned shortcuts. First, from launcher 1.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList(), getCallingUser());
+
+ assertEquals(0,
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
+ assertEquals(0,
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), "s1");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s2");
+ });
+
+ // Clear all pins from launcher 2.
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList(), getCallingUser());
+
+ assertEquals(0,
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
+ assertEquals(0,
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()).size());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), "s1");
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
}
public void testCreateShortcutIntent() {
@@ -1880,8 +2139,11 @@
}
public void testLauncherCallback() throws Throwable {
- LauncherApps.Callback c0 = mock(LauncherApps.Callback.class);
+ // TODO Add "multi" version -- run the test with two launchers and make sure the callback
+ // argument only contains the ones that are actually visible to each launcher.
+
+ LauncherApps.Callback c0 = mock(LauncherApps.Callback.class);
// Set listeners