Merge "AudioFormat: implement ENCODING_IEC61937" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index ab52161..617aac0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8512,6 +8512,7 @@
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -19221,7 +19222,6 @@
method public double getElevationUncertaintyInDeg();
method public byte getLossOfLock();
method public byte getMultipathIndicator();
- method public byte getPrn();
method public double getPseudorangeInMeters();
method public double getPseudorangeRateCarrierInMetersPerSec();
method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -19232,6 +19232,7 @@
method public long getReceivedGpsTowUncertaintyInNs();
method public double getSnrInDb();
method public short getState();
+ method public short getSvid();
method public short getTimeFromLastBitInMs();
method public double getTimeOffsetInNs();
method public boolean hasAzimuthInDeg();
@@ -19291,7 +19292,6 @@
method public void setElevationUncertaintyInDeg(double);
method public void setLossOfLock(byte);
method public void setMultipathIndicator(byte);
- method public void setPrn(byte);
method public void setPseudorangeInMeters(double);
method public void setPseudorangeRateCarrierInMetersPerSec(double);
method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -19302,6 +19302,7 @@
method public void setReceivedGpsTowUncertaintyInNs(long);
method public void setSnrInDb(double);
method public void setState(short);
+ method public void setSvid(short);
method public void setTimeFromLastBitInMs(short);
method public void setTimeOffsetInNs(double);
method public void setUsedInFix(boolean);
@@ -19356,17 +19357,17 @@
method public int describeContents();
method public byte[] getData();
method public short getMessageId();
- method public byte getPrn();
method public short getStatus();
method public short getSubmessageId();
+ method public short getSvid();
method public byte getType();
method public void reset();
method public void set(android.location.GnssNavigationMessage);
method public void setData(byte[]);
method public void setMessageId(short);
- method public void setPrn(byte);
method public void setStatus(short);
method public void setSubmessageId(short);
+ method public void setSvid(short);
method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -19412,8 +19413,8 @@
method public int getConstellationType(int);
method public float getElevation(int);
method public int getNumSatellites();
- method public int getPrn(int);
method public float getSnr(int);
+ method public int getSvid(int);
method public boolean hasAlmanac(int);
method public boolean hasEphemeris(int);
method public boolean usedInFix(int);
@@ -22950,10 +22951,8 @@
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
- field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
- field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
- field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
- field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -23053,6 +23052,8 @@
public static abstract class TvRecordingClient.RecordingCallback {
ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnectionFailed(java.lang.String);
+ method public void onDisconnected(java.lang.String);
method public void onError(int);
method public void onRecordingStopped(android.net.Uri);
method public void onTuned();
@@ -51478,7 +51479,6 @@
method public static int getLength(java.lang.Object);
method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
- method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -56668,13 +56668,11 @@
method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
method public java.lang.String getInternationalCurrencySymbol();
method public char getMinusSign();
- method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
method public java.lang.String getNaN();
method public char getPatternSeparator();
method public char getPerMill();
method public char getPercent();
- method public java.lang.String getPercentString();
method public char getZeroDigit();
method public void setCurrency(java.util.Currency);
method public void setCurrencySymbol(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 8ea1854..ec422c0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8818,6 +8818,7 @@
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -20401,7 +20402,6 @@
method public double getElevationUncertaintyInDeg();
method public byte getLossOfLock();
method public byte getMultipathIndicator();
- method public byte getPrn();
method public double getPseudorangeInMeters();
method public double getPseudorangeRateCarrierInMetersPerSec();
method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -20412,6 +20412,7 @@
method public long getReceivedGpsTowUncertaintyInNs();
method public double getSnrInDb();
method public short getState();
+ method public short getSvid();
method public short getTimeFromLastBitInMs();
method public double getTimeOffsetInNs();
method public boolean hasAzimuthInDeg();
@@ -20471,7 +20472,6 @@
method public void setElevationUncertaintyInDeg(double);
method public void setLossOfLock(byte);
method public void setMultipathIndicator(byte);
- method public void setPrn(byte);
method public void setPseudorangeInMeters(double);
method public void setPseudorangeRateCarrierInMetersPerSec(double);
method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -20482,6 +20482,7 @@
method public void setReceivedGpsTowUncertaintyInNs(long);
method public void setSnrInDb(double);
method public void setState(short);
+ method public void setSvid(short);
method public void setTimeFromLastBitInMs(short);
method public void setTimeOffsetInNs(double);
method public void setUsedInFix(boolean);
@@ -20536,17 +20537,17 @@
method public int describeContents();
method public byte[] getData();
method public short getMessageId();
- method public byte getPrn();
method public short getStatus();
method public short getSubmessageId();
+ method public short getSvid();
method public byte getType();
method public void reset();
method public void set(android.location.GnssNavigationMessage);
method public void setData(byte[]);
method public void setMessageId(short);
- method public void setPrn(byte);
method public void setStatus(short);
method public void setSubmessageId(short);
+ method public void setSvid(short);
method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -20592,8 +20593,8 @@
method public int getConstellationType(int);
method public float getElevation(int);
method public int getNumSatellites();
- method public int getPrn(int);
method public float getSnr(int);
+ method public int getSvid(int);
method public boolean hasAlmanac(int);
method public boolean hasEphemeris(int);
method public boolean usedInFix(int);
@@ -24649,10 +24650,8 @@
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
- field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
- field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
- field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
- field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -24811,6 +24810,8 @@
public static abstract class TvRecordingClient.RecordingCallback {
ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnectionFailed(java.lang.String);
+ method public void onDisconnected(java.lang.String);
method public void onError(int);
method public void onEvent(java.lang.String, java.lang.String, android.os.Bundle);
method public void onRecordingStopped(android.net.Uri);
@@ -54567,7 +54568,6 @@
method public static int getLength(java.lang.Object);
method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
- method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -59757,13 +59757,11 @@
method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
method public java.lang.String getInternationalCurrencySymbol();
method public char getMinusSign();
- method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
method public java.lang.String getNaN();
method public char getPatternSeparator();
method public char getPerMill();
method public char getPercent();
- method public java.lang.String getPercentString();
method public char getZeroDigit();
method public void setCurrency(java.util.Currency);
method public void setCurrencySymbol(java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index e716292..d202108 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -8517,6 +8517,7 @@
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -19229,7 +19230,6 @@
method public double getElevationUncertaintyInDeg();
method public byte getLossOfLock();
method public byte getMultipathIndicator();
- method public byte getPrn();
method public double getPseudorangeInMeters();
method public double getPseudorangeRateCarrierInMetersPerSec();
method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -19240,6 +19240,7 @@
method public long getReceivedGpsTowUncertaintyInNs();
method public double getSnrInDb();
method public short getState();
+ method public short getSvid();
method public short getTimeFromLastBitInMs();
method public double getTimeOffsetInNs();
method public boolean hasAzimuthInDeg();
@@ -19299,7 +19300,6 @@
method public void setElevationUncertaintyInDeg(double);
method public void setLossOfLock(byte);
method public void setMultipathIndicator(byte);
- method public void setPrn(byte);
method public void setPseudorangeInMeters(double);
method public void setPseudorangeRateCarrierInMetersPerSec(double);
method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -19310,6 +19310,7 @@
method public void setReceivedGpsTowUncertaintyInNs(long);
method public void setSnrInDb(double);
method public void setState(short);
+ method public void setSvid(short);
method public void setTimeFromLastBitInMs(short);
method public void setTimeOffsetInNs(double);
method public void setUsedInFix(boolean);
@@ -19364,17 +19365,17 @@
method public int describeContents();
method public byte[] getData();
method public short getMessageId();
- method public byte getPrn();
method public short getStatus();
method public short getSubmessageId();
+ method public short getSvid();
method public byte getType();
method public void reset();
method public void set(android.location.GnssNavigationMessage);
method public void setData(byte[]);
method public void setMessageId(short);
- method public void setPrn(byte);
method public void setStatus(short);
method public void setSubmessageId(short);
+ method public void setSvid(short);
method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -19420,8 +19421,8 @@
method public int getConstellationType(int);
method public float getElevation(int);
method public int getNumSatellites();
- method public int getPrn(int);
method public float getSnr(int);
+ method public int getSvid(int);
method public boolean hasAlmanac(int);
method public boolean hasEphemeris(int);
method public boolean usedInFix(int);
@@ -22959,10 +22960,8 @@
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
- field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
- field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
- field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
- field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -23062,6 +23061,8 @@
public static abstract class TvRecordingClient.RecordingCallback {
ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnectionFailed(java.lang.String);
+ method public void onDisconnected(java.lang.String);
method public void onError(int);
method public void onRecordingStopped(android.net.Uri);
method public void onTuned();
@@ -51495,7 +51496,6 @@
method public static int getLength(java.lang.Object);
method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
- method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -56685,13 +56685,11 @@
method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
method public java.lang.String getInternationalCurrencySymbol();
method public char getMinusSign();
- method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
method public java.lang.String getNaN();
method public char getPatternSeparator();
method public char getPerMill();
method public char getPercent();
- method public java.lang.String getPercentString();
method public char getZeroDigit();
method public void setCurrency(java.util.Currency);
method public void setCurrencySymbol(java.lang.String);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 622012e..ea58e29 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,6 +16,8 @@
package android.app;
+import static java.lang.Character.MIN_VALUE;
+
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
@@ -26,20 +28,6 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.StyleRes;
-import android.os.PersistableBundle;
-import android.transition.Scene;
-import android.transition.TransitionManager;
-import android.util.ArrayMap;
-import android.util.SuperNotCalledException;
-import android.view.DragEvent;
-import android.view.DropPermissions;
-import android.view.Window.WindowControllerCallback;
-import android.widget.Toolbar;
-
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.app.ToolbarActionBar;
-
import android.annotation.SystemApi;
import android.app.admin.DevicePolicyManager;
import android.app.assist.AssistContent;
@@ -61,7 +49,12 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.ShapeDrawable;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.net.Uri;
@@ -71,6 +64,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.UserHandle;
@@ -78,16 +72,22 @@
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
+import android.transition.Scene;
+import android.transition.TransitionManager;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.EventLog;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SuperNotCalledException;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextThemeWrapper;
+import android.view.DragEvent;
+import android.view.DropPermissions;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
@@ -104,11 +104,17 @@
import android.view.ViewManager;
import android.view.ViewRootImpl;
import android.view.Window;
+import android.view.Window.WindowControllerCallback;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
+import android.widget.Toolbar;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ToolbarActionBar;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
import java.io.FileDescriptor;
@@ -119,8 +125,6 @@
import java.util.HashMap;
import java.util.List;
-import static java.lang.Character.MIN_VALUE;
-
/**
* An activity is a single, focused thing that the user can do. Almost all
* activities interact with the user, so the Activity class takes care of
@@ -3974,17 +3978,52 @@
// Get the primary color and update the TaskDescription for this activity
if (theme != null) {
TypedArray a = theme.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+ int windowBgResourceId = a.getResourceId(
+ com.android.internal.R.styleable.Window_windowBackground, 0);
+ int windowBgFallbackResourceId = a.getResourceId(
+ com.android.internal.R.styleable.Window_windowBackgroundFallback, 0);
int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0);
+ int colorBg = tryExtractColorFromDrawable(DecorView.getResizingBackgroundDrawable(this,
+ windowBgResourceId, windowBgFallbackResourceId));
a.recycle();
if (colorPrimary != 0) {
- ActivityManager.TaskDescription v = new ActivityManager.TaskDescription(null, null,
- colorPrimary);
- setTaskDescription(v);
+ ActivityManager.TaskDescription td = new ActivityManager.TaskDescription();
+ td.setPrimaryColor(colorPrimary);
+ td.setBackgroundColor(colorBg);
+ setTaskDescription(td);
}
}
}
/**
+ * Attempts to extract the color from a given drawable.
+ *
+ * @return the extracted color or 0 if no color could be extracted.
+ */
+ private int tryExtractColorFromDrawable(Drawable drawable) {
+ if (drawable instanceof ColorDrawable) {
+ return ((ColorDrawable) drawable).getColor();
+ } else if (drawable instanceof InsetDrawable) {
+ return tryExtractColorFromDrawable(((InsetDrawable) drawable).getDrawable());
+ } else if (drawable instanceof ShapeDrawable) {
+ Paint p = ((ShapeDrawable) drawable).getPaint();
+ if (p != null) {
+ return p.getColor();
+ }
+ } else if (drawable instanceof LayerDrawable) {
+ LayerDrawable ld = (LayerDrawable) drawable;
+ int numLayers = ld.getNumberOfLayers();
+ for (int i = 0; i < numLayers; i++) {
+ int color = tryExtractColorFromDrawable(ld.getDrawable(i));
+ if (color != 0) {
+ return color;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
* Requests permissions to be granted to this application. These permissions
* must be requested in your manifest, they should not be granted to your app,
* and they should have protection level {@link android.content.pm.PermissionInfo
@@ -5612,8 +5651,8 @@
if (taskDescription.getIconFilename() == null && taskDescription.getIcon() != null) {
final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
final Bitmap icon = Bitmap.createScaledBitmap(taskDescription.getIcon(), size, size, true);
- td = new ActivityManager.TaskDescription(taskDescription.getLabel(), icon,
- taskDescription.getPrimaryColor());
+ td = new ActivityManager.TaskDescription(taskDescription);
+ td.setIcon(icon);
} else {
td = taskDescription;
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 1eb2fe2..dd73261 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -632,6 +632,17 @@
public static boolean useWindowFrameForBackdrop(int stackId) {
return stackId == FREEFORM_WORKSPACE_STACK_ID || stackId == PINNED_STACK_ID;
}
+
+ /**
+ * Returns true if a window from the specified stack with {@param stackId} are normally
+ * fullscreen, i. e. they can become the top opaque fullscreen window, meaning that it
+ * controls system bars, lockscreen occluded/dismissing state, screen rotation animation,
+ * etc.
+ */
+ public static boolean normallyFullscreenWindows(int stackId) {
+ return stackId != PINNED_STACK_ID && stackId != FREEFORM_WORKSPACE_STACK_ID
+ && stackId != DOCKED_STACK_ID;
+ }
}
/**
@@ -863,8 +874,10 @@
public static final String ATTR_TASKDESCRIPTION_PREFIX = "task_description_";
private static final String ATTR_TASKDESCRIPTIONLABEL =
ATTR_TASKDESCRIPTION_PREFIX + "label";
- private static final String ATTR_TASKDESCRIPTIONCOLOR =
+ private static final String ATTR_TASKDESCRIPTIONCOLOR_PRIMARY =
ATTR_TASKDESCRIPTION_PREFIX + "color";
+ private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND =
+ ATTR_TASKDESCRIPTION_PREFIX + "colorBackground";
private static final String ATTR_TASKDESCRIPTIONICONFILENAME =
ATTR_TASKDESCRIPTION_PREFIX + "icon_filename";
@@ -872,28 +885,21 @@
private Bitmap mIcon;
private String mIconFilename;
private int mColorPrimary;
+ private int mColorBackground;
/**
* Creates the TaskDescription to the specified values.
*
* @param label A label and description of the current state of this task.
* @param icon An icon that represents the current state of this task.
- * @param colorPrimary A color to override the theme's primary color. This color must be opaque.
+ * @param colorPrimary A color to override the theme's primary color. This color must be
+ * opaque.
*/
public TaskDescription(String label, Bitmap icon, int colorPrimary) {
+ this(label, icon, null, colorPrimary, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
-
- mLabel = label;
- mIcon = icon;
- mColorPrimary = colorPrimary;
- }
-
- /** @hide */
- public TaskDescription(String label, int colorPrimary, String iconFilename) {
- this(label, null, colorPrimary);
- mIconFilename = iconFilename;
}
/**
@@ -903,7 +909,7 @@
* @param icon An icon that represents the current state of this activity.
*/
public TaskDescription(String label, Bitmap icon) {
- this(label, icon, 0);
+ this(label, icon, null, 0, 0);
}
/**
@@ -912,14 +918,24 @@
* @param label A label and description of the current state of this activity.
*/
public TaskDescription(String label) {
- this(label, null, 0);
+ this(label, null, null, 0, 0);
}
/**
* Creates an empty TaskDescription.
*/
public TaskDescription() {
- this(null, null, 0);
+ this(null, null, null, 0, 0);
+ }
+
+ /** @hide */
+ public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary,
+ int colorBackground) {
+ mLabel = label;
+ mIcon = icon;
+ mIconFilename = iconFilename;
+ mColorPrimary = colorPrimary;
+ mColorBackground = colorBackground;
}
/**
@@ -928,8 +944,9 @@
public TaskDescription(TaskDescription td) {
mLabel = td.mLabel;
mIcon = td.mIcon;
- mColorPrimary = td.mColorPrimary;
mIconFilename = td.mIconFilename;
+ mColorPrimary = td.mColorPrimary;
+ mColorBackground = td.mColorBackground;
}
private TaskDescription(Parcel source) {
@@ -957,6 +974,18 @@
}
/**
+ * Sets the background color for this task description.
+ * @hide
+ */
+ public void setBackgroundColor(int backgroundColor) {
+ // Ensure that the given color is valid
+ if ((backgroundColor != 0) && (Color.alpha(backgroundColor) != 255)) {
+ throw new RuntimeException("A TaskDescription's background color should be opaque");
+ }
+ mColorBackground = backgroundColor;
+ }
+
+ /**
* Sets the icon for this task description.
* @hide
*/
@@ -1005,8 +1034,8 @@
public static Bitmap loadTaskDescriptionIcon(String iconFilename, int userId) {
if (iconFilename != null) {
try {
- return ActivityManagerNative.getDefault().
- getTaskDescriptionIcon(iconFilename, userId);
+ return ActivityManagerNative.getDefault().getTaskDescriptionIcon(iconFilename,
+ userId);
} catch (RemoteException e) {
}
}
@@ -1020,13 +1049,26 @@
return mColorPrimary;
}
+ /**
+ * @return The background color.
+ * @hide
+ */
+ public int getBackgroundColor() {
+ return mColorBackground;
+ }
+
/** @hide */
public void saveToXml(XmlSerializer out) throws IOException {
if (mLabel != null) {
out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, mLabel);
}
if (mColorPrimary != 0) {
- out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(mColorPrimary));
+ out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_PRIMARY,
+ Integer.toHexString(mColorPrimary));
+ }
+ if (mColorBackground != 0) {
+ out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND,
+ Integer.toHexString(mColorBackground));
}
if (mIconFilename != null) {
out.attribute(null, ATTR_TASKDESCRIPTIONICONFILENAME, mIconFilename);
@@ -1037,8 +1079,10 @@
public void restoreFromXml(String attrName, String attrValue) {
if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) {
setLabel(attrValue);
- } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) {
+ } else if (ATTR_TASKDESCRIPTIONCOLOR_PRIMARY.equals(attrName)) {
setPrimaryColor((int) Long.parseLong(attrValue, 16));
+ } else if (ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND.equals(attrName)) {
+ setBackgroundColor((int) Long.parseLong(attrValue, 16));
} else if (ATTR_TASKDESCRIPTIONICONFILENAME.equals(attrName)) {
setIconFilename(attrValue);
}
@@ -1064,6 +1108,7 @@
mIcon.writeToParcel(dest, 0);
}
dest.writeInt(mColorPrimary);
+ dest.writeInt(mColorBackground);
if (mIconFilename == null) {
dest.writeInt(0);
} else {
@@ -1076,6 +1121,7 @@
mLabel = source.readInt() > 0 ? source.readString() : null;
mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
mColorPrimary = source.readInt();
+ mColorBackground = source.readInt();
mIconFilename = source.readInt() > 0 ? source.readString() : null;
}
@@ -1092,7 +1138,8 @@
@Override
public String toString() {
return "TaskDescription Label: " + mLabel + " Icon: " + mIcon +
- " colorPrimary: " + mColorPrimary;
+ " IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary +
+ " colorBackground: " + mColorBackground;
}
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 0d35cf0..3c8dfce 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -56,7 +56,7 @@
void setImportance(String pkg, int uid, in Notification.Topic topic, int importance);
int getImportance(String pkg, int uid, in Notification.Topic topic);
int getTopicImportance(String pkg, String topicId);
- boolean doesAppUseTopics(String pkg, int uid);
+ boolean doesUserUseTopics(String pkg, int uid);
boolean hasBannedTopics(String pkg, int uid);
// TODO: Remove this when callers have been migrated to the equivalent
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 55c6353..35b7c39 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3358,13 +3358,9 @@
return mN.bigContentView;
} else if (mStyle != null) {
result = mStyle.makeBigContentView();
- } else if (mActions.size() == 0) {
- return null;
- }
- if (result == null) {
- result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
- } else {
hideLine1Text(result);
+ } else if (mActions.size() != 0) {
+ result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
}
adaptNotificationHeaderForBigContentView(result);
return result;
@@ -3384,11 +3380,15 @@
}
private void hideLine1Text(RemoteViews result) {
- result.setViewVisibility(R.id.text_line_1, View.GONE);
+ if (result != null) {
+ result.setViewVisibility(R.id.text_line_1, View.GONE);
+ }
}
private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
- result.setBoolean(R.id.notification_header, "setExpanded", true);
+ if (result != null) {
+ result.setBoolean(R.id.notification_header, "setExpanded", true);
+ }
}
/**
@@ -4326,6 +4326,15 @@
return makeMediaBigContentView();
}
+ /**
+ * @hide
+ */
+ @Override
+ public RemoteViews makeHeadsUpContentView() {
+ RemoteViews expanded = makeMediaBigContentView();
+ return expanded != null ? expanded : makeMediaContentView();
+ }
+
/** @hide */
@Override
public void addExtras(Bundle extras) {
@@ -4407,6 +4416,13 @@
private RemoteViews makeMediaBigContentView() {
final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
+ // Dont add an expanded view if there is no more content to be revealed
+ int actionsInCompact = mActionsToShowInCompact == null
+ ? 0
+ : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
+ if (mBuilder.mN.mLargeIcon == null && actionCount <= actionsInCompact) {
+ return null;
+ }
RemoteViews big = mBuilder.applyStandardTemplate(
R.layout.notification_template_material_big_media,
false);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1b2322f..f53170a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1826,9 +1826,23 @@
* this method; if it has not, a security exception will be thrown.
*/
public int getCurrentFailedPasswordAttempts() {
+ return getCurrentFailedPasswordAttempts(myUserId());
+ }
+
+ /**
+ * Retrieve the number of times the given user has failed at entering a
+ * password since that last successful password entry.
+ *
+ * <p>The calling device admin must have requested
+ * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to be able to call this method; if it has
+ * not and it is not the system uid, a security exception will be thrown.
+ *
+ * @hide
+ */
+ public int getCurrentFailedPasswordAttempts(int userHandle) {
if (mService != null) {
try {
- return mService.getCurrentFailedPasswordAttempts(myUserId(), mParentInstance);
+ return mService.getCurrentFailedPasswordAttempts(userHandle, mParentInstance);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0f6f856..b476a25 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3028,6 +3028,17 @@
"android.intent.action.MANAGED_PROFILE_REMOVED";
/**
+ * Broadcast sent to the primary user when the credential-encrypted private storage for
+ * an associated managed profile is unlocked. Carries an extra {@link #EXTRA_USER} that
+ * specifies the UserHandle of the profile that was unlocked. Only applications (for example
+ * Launchers) that need to display merged content across both primary and managed profiles
+ * need to worry about this broadcast. This is only sent to registered receivers,
+ * not manifest receivers.
+ */
+ public static final String ACTION_MANAGED_PROFILE_UNLOCKED =
+ "android.intent.action.MANAGED_PROFILE_UNLOCKED";
+
+ /**
* Broadcast sent to the primary user when an associated managed profile's availability has
* changed. This includes when the user toggles the profile's quiet mode. Carries an extra
* {@link #EXTRA_USER} that specifies the UserHandle of the profile. When quiet mode is changed,
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 8468040..2ca7589 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1201,7 +1201,7 @@
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
sourceDocumentUri.getAuthority());
try {
- return moveDocument(client, sourceParentDocumentUri, sourceDocumentUri,
+ return moveDocument(client, sourceDocumentUri, sourceParentDocumentUri,
targetParentDocumentUri);
} catch (Exception e) {
Log.w(TAG, "Failed to move document", e);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7830142..3169bf4 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4660,6 +4660,14 @@
"lock_screen_allow_private_notifications";
/**
+ * When set by a user, allows notification remote input atop a securely locked screen
+ * without having to unlock
+ * @hide
+ */
+ public static final String LOCK_SCREEN_ALLOW_REMOTE_INPUT =
+ "lock_screen_allow_remote_input";
+
+ /**
* Set by the system to track if the user needs to see the call to action for
* the lockscreen notification policy.
* @hide
@@ -5139,14 +5147,6 @@
public static final String TTS_DEFAULT_SYNTH = "tts_default_synth";
/**
- * Whether text-to-speech higher speech rate is enabled.
- * 0 = disabled.
- * 1 = enabled.
- * @hide
- */
- public static final String TTS_DEFAULT_HIGHER_SPEECH_RATE_ENABLED =
- "tts_default_higher_speech_rate_enabled";
- /**
* Default text-to-speech language.
*
* @deprecated this setting is no longer in use, as of the Ice Cream
@@ -5907,13 +5907,6 @@
"camera_double_tap_power_gesture_disabled";
/**
- * Name of the package used as WebView provider (if unset the provider is instead determined
- * by the system).
- * @hide
- */
- public static final String WEBVIEW_PROVIDER = "webview_provider";
-
- /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -5958,7 +5951,6 @@
ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
TTS_USE_DEFAULTS,
TTS_DEFAULT_RATE,
- TTS_DEFAULT_HIGHER_SPEECH_RATE_ENABLED,
TTS_DEFAULT_PITCH,
TTS_DEFAULT_SYNTH,
TTS_DEFAULT_LANG,
@@ -6940,6 +6932,13 @@
public static final String WEBVIEW_DATA_REDUCTION_PROXY_KEY =
"webview_data_reduction_proxy_key";
+ /**
+ * Name of the package used as WebView provider (if unset the provider is instead determined
+ * by the system).
+ * @hide
+ */
+ public static final String WEBVIEW_PROVIDER = "webview_provider";
+
/**
* Whether Wifi display is enabled/disabled
* 0=disabled. 1=enabled.
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index e9d12f5..409994d 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -54,6 +54,9 @@
import java.io.IOException;
import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -691,6 +694,22 @@
private static Pattern sBackgroundColorPattern;
private static Pattern sTextDecorationPattern;
+ /**
+ * Name-value mapping of HTML/CSS colors which have different values in {@link Color}.
+ */
+ private static final Map<String, Integer> sColorMap;
+
+ static {
+ sColorMap = new HashMap<>();
+ sColorMap.put("darkgray", 0xFFA9A9A9);
+ sColorMap.put("gray", 0xFF808080);
+ sColorMap.put("lightgray", 0xFFD3D3D3);
+ sColorMap.put("darkgrey", 0xFFA9A9A9);
+ sColorMap.put("grey", 0xFF808080);
+ sColorMap.put("lightgrey", 0xFFD3D3D3);
+ sColorMap.put("green", 0xFF008000);
+ }
+
private static Pattern getTextAlignPattern() {
if (sTextAlignPattern == null) {
sTextAlignPattern = Pattern.compile("(?:\\s+|\\A)text-align\\s*:\\s*(\\S*)\\b");
@@ -948,7 +967,7 @@
final int len = text.length();
if (margin > 0) {
appendNewlines(text, margin);
- text.setSpan(new Newline(margin), len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Newline(margin));
}
String style = attributes.getValue("", "style");
@@ -957,14 +976,11 @@
if (m.find()) {
String alignment = m.group(1);
if (alignment.equalsIgnoreCase("start")) {
- text.setSpan(new Alignment(Layout.Alignment.ALIGN_NORMAL),
- len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Alignment(Layout.Alignment.ALIGN_NORMAL));
} else if (alignment.equalsIgnoreCase("center")) {
- text.setSpan(new Alignment(Layout.Alignment.ALIGN_CENTER),
- len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Alignment(Layout.Alignment.ALIGN_CENTER));
} else if (alignment.equalsIgnoreCase("end")) {
- text.setSpan(new Alignment(Layout.Alignment.ALIGN_OPPOSITE),
- len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Alignment(Layout.Alignment.ALIGN_OPPOSITE));
}
}
}
@@ -1053,7 +1069,7 @@
private static void start(Editable text, Object mark) {
int len = text.length();
- text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);
+ text.setSpan(mark, len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
private static void end(Editable text, Class kind, Object repl) {
@@ -1064,25 +1080,22 @@
}
}
- private static void startCssStyle(Editable text, Attributes attributes) {
+ private void startCssStyle(Editable text, Attributes attributes) {
String style = attributes.getValue("", "style");
if (style != null) {
- final int len = text.length();
Matcher m = getForegroundColorPattern().matcher(style);
if (m.find()) {
- int c = Color.getHtmlColor(m.group(1));
+ int c = getHtmlColor(m.group(1));
if (c != -1) {
- text.setSpan(new Foreground(c | 0xFF000000), len, len,
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Foreground(c | 0xFF000000));
}
}
m = getBackgroundColorPattern().matcher(style);
if (m.find()) {
- int c = Color.getHtmlColor(m.group(1));
+ int c = getHtmlColor(m.group(1));
if (c != -1) {
- text.setSpan(new Background(c | 0xFF000000), len, len,
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Background(c | 0xFF000000));
}
}
@@ -1090,7 +1103,7 @@
if (m.find()) {
String textDecoration = m.group(1);
if (textDecoration.equalsIgnoreCase("line-through")) {
- text.setSpan(new Strikethrough(), len, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Strikethrough());
}
}
}
@@ -1134,58 +1147,41 @@
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
- private static void startFont(Editable text, Attributes attributes) {
+ private void startFont(Editable text, Attributes attributes) {
String color = attributes.getValue("", "color");
String face = attributes.getValue("", "face");
- int len = text.length();
- text.setSpan(new Font(color, face), len, len, Spannable.SPAN_MARK_MARK);
+ if (!TextUtils.isEmpty(color)) {
+ int c = getHtmlColor(color);
+ if (c != -1) {
+ start(text, new Foreground(c | 0xFF000000));
+ }
+ }
+
+ if (!TextUtils.isEmpty(face)) {
+ start(text, new Font(face));
+ }
}
private static void endFont(Editable text) {
- int len = text.length();
- Font f = getLast(text, Font.class);
- int where = text.getSpanStart(f);
- text.removeSpan(f);
+ Font font = getLast(text, Font.class);
+ if (font != null) {
+ setSpanFromMark(text, font, new TypefaceSpan(font.mFace));
+ }
- if (where != len) {
- if (!TextUtils.isEmpty(f.mColor)) {
- if (f.mColor.startsWith("@")) {
- Resources res = Resources.getSystem();
- String name = f.mColor.substring(1);
- int colorRes = res.getIdentifier(name, "color", "android");
- if (colorRes != 0) {
- ColorStateList colors = res.getColorStateList(colorRes, null);
- text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null),
- where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- } else {
- int c = Color.getHtmlColor(f.mColor);
- if (c != -1) {
- text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
- where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
- }
-
- if (f.mFace != null) {
- text.setSpan(new TypefaceSpan(f.mFace), where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
+ Foreground foreground = getLast(text, Foreground.class);
+ if (foreground != null) {
+ setSpanFromMark(text, foreground,
+ new ForegroundColorSpan(foreground.mForegroundColor));
}
}
private static void startA(Editable text, Attributes attributes) {
String href = attributes.getValue("", "href");
-
- int len = text.length();
- text.setSpan(new Href(href), len, len, Spannable.SPAN_MARK_MARK);
+ start(text, new Href(href));
}
private static void endA(Editable text) {
- int len = text.length();
Href h = getLast(text, Href.class);
if (h != null) {
if (h.mHref != null) {
@@ -1194,6 +1190,17 @@
}
}
+ private int getHtmlColor(String color) {
+ if ((mFlags & Html.FROM_HTML_OPTION_USE_CSS_COLORS)
+ == Html.FROM_HTML_OPTION_USE_CSS_COLORS) {
+ Integer i = sColorMap.get(color.toLowerCase(Locale.US));
+ if (i != null) {
+ return i;
+ }
+ }
+ return Color.getHtmlColor(color);
+ }
+
public void setDocumentLocator(Locator locator) {
}
@@ -1278,11 +1285,9 @@
private static class Bullet { }
private static class Font {
- public String mColor;
public String mFace;
- public Font(String color, String face) {
- mColor = color;
+ public Font(String face) {
mFace = face;
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a296051..152dd66 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -224,7 +224,7 @@
mParent.requestTransparentRegion(this);
mSession = getWindowSession();
mLayout.token = getWindowToken();
- mLayout.setTitle("SurfaceView");
+ mLayout.setTitle("SurfaceView - " + getViewRootImpl().getTitle());
mViewVisibility = getVisibility() == VISIBLE;
if (!mGlobalListenersAdded) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2612ab2..bba5a17 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6874,6 +6874,15 @@
* @param info The info whose drawing order should be populated
*/
private void populateAccessibilityNodeInfoDrawingOrderInParent(AccessibilityNodeInfo info) {
+ /*
+ * If the view's bounds haven't been set yet, layout has not completed. In that situation,
+ * drawing order may not be well-defined, and some Views with custom drawing order may
+ * not be initialized sufficiently to respond properly getChildDrawingOrder.
+ */
+ if ((mPrivateFlags & PFLAG_HAS_BOUNDS) == 0) {
+ info.setDrawingOrder(0);
+ return;
+ }
int drawingOrderInParent = 1;
// Iterate up the hierarchy if parents are not important for a11y
View viewAtDrawingLevel = this;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 947906b..609c471 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.SystemApi;
+import android.app.ActivityManager.StackId;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
@@ -388,6 +389,12 @@
* Check whether the window is currently dimming.
*/
public boolean isDimming();
+
+ /**
+ * @return the stack id this windows belongs to, or {@link StackId#INVALID_STACK_ID} if
+ * not attached to any stack.
+ */
+ int getStackId();
}
/**
@@ -465,6 +472,11 @@
* @return The content insets of the docked divider window.
*/
int getDockedDividerInsetsLw();
+
+ /**
+ * Retrieves the {@param outBounds} from the stack with id {@param stackId}.
+ */
+ void getStackBounds(int stackId, Rect outBounds);
}
public interface PointerEventListener {
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index 3f50fe2..94e8b70 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -97,22 +97,12 @@
*/
public boolean isEnabled() {
try {
- PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
- int enabled_state = pm.getApplicationEnabledSetting(packageName);
- switch (enabled_state) {
- case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
- return true;
- case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
- ApplicationInfo applicationInfo = getPackageInfo().applicationInfo;
- return applicationInfo.enabled;
- default:
- return false;
- }
+ // Explicitly fetch up-to-date package info here since the enabled-state of the package
+ // might have changed since we last fetched its package info.
+ updatePackageInfo();
+ return getPackageInfo().applicationInfo.enabled;
} catch (WebViewPackageNotFoundException e) {
return false;
- } catch (IllegalArgumentException e) {
- // Thrown by PackageManager.getApplicationEnabledSetting if the package does not exist
- return false;
}
}
@@ -124,14 +114,18 @@
return availableByDefault;
}
+ private void updatePackageInfo() {
+ try {
+ PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+ packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new WebViewPackageNotFoundException(e);
+ }
+ }
+
public PackageInfo getPackageInfo() {
if (packageInfo == null) {
- try {
- PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
- packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
- } catch (PackageManager.NameNotFoundException e) {
- throw new WebViewPackageNotFoundException(e);
- }
+ updatePackageInfo();
}
return packageInfo;
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 45fc6c3..3796df7 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -36,6 +36,8 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.speech.RecognizerIntent;
import android.text.Editable;
import android.text.InputType;
@@ -1332,6 +1334,48 @@
setIconified(false);
}
+ static class SavedState extends BaseSavedState {
+ boolean isIconified;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ public SavedState(Parcel source) {
+ super(source);
+ isIconified = (Boolean) source.readValue(null);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeValue(isIconified);
+ }
+
+ @Override
+ public String toString() {
+ return "SearchView.SavedState{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " isIconified=" + isIconified + "}";
+ }
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ ss.isIconified = isIconified();
+ return ss;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ updateViewsVisibility(ss.isIconified);
+ requestLayout();
+ }
+
@Override
public CharSequence getAccessibilityClassName() {
return SearchView.class.getName();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 3402989..8c3c2b5 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -129,6 +129,7 @@
if (mServiceConnections.isEmpty()) {
mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
sendVoiceChoicesIfNeeded();
+ mChooserListAdapter.setShowServiceTargets(true);
}
break;
@@ -138,6 +139,7 @@
}
unbindRemainingServices();
sendVoiceChoicesIfNeeded();
+ mChooserListAdapter.setShowServiceTargets(true);
break;
default:
@@ -766,6 +768,7 @@
private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
private final List<TargetInfo> mCallerTargets = new ArrayList<>();
+ private boolean mShowServiceTargets;
private float mLateFee = 1.f;
@@ -866,6 +869,9 @@
}
public int getServiceTargetCount() {
+ if (!mShowServiceTargets) {
+ return 0;
+ }
return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS);
}
@@ -955,6 +961,14 @@
notifyDataSetChanged();
}
+ /**
+ * Set to true to reveal all service targets at once.
+ */
+ public void setShowServiceTargets(boolean show) {
+ mShowServiceTargets = show;
+ notifyDataSetChanged();
+ }
+
private void insertServiceTarget(ChooserTargetInfo chooserTargetInfo) {
final float newScore = chooserTargetInfo.getModifiedScore();
for (int i = 0, N = mServiceTargets.size(); i < N; i++) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 88af920..af3f7ec 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -86,6 +86,7 @@
import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
@@ -1002,13 +1003,25 @@
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+ // If we didn't request fullscreen layout, but we still got it because of the
+ // mForceWindowDrawsStatusBarBackground flag, also consume top inset.
+ boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
+ && (sysUiVisibility & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
+ && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
+ && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
+ && mForceWindowDrawsStatusBarBackground
+ && mLastTopInset != 0;
+
+ int consumedTop = consumingStatusBar ? mLastTopInset : 0;
int consumedRight = consumingNavBar ? mLastRightInset : 0;
int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
if (mContentRoot != null
&& mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
- if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) {
+ if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
+ || lp.bottomMargin != consumedBottom) {
+ lp.topMargin = consumedTop;
lp.rightMargin = consumedRight;
lp.bottomMargin = consumedBottom;
mContentRoot.setLayoutParams(lp);
@@ -1022,7 +1035,7 @@
if (insets != null) {
insets = insets.replaceSystemWindowInsets(
insets.getSystemWindowInsetLeft(),
- insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetTop() - consumedTop,
insets.getSystemWindowInsetRight() - consumedRight,
insets.getSystemWindowInsetBottom() - consumedBottom);
}
@@ -1718,8 +1731,13 @@
private void loadBackgroundDrawablesIfNeeded() {
if (mResizingBackgroundDrawable == null) {
- mResizingBackgroundDrawable = getResizingBackgroundDrawable(
+ mResizingBackgroundDrawable = getResizingBackgroundDrawable(getContext(),
mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
+ if (mResizingBackgroundDrawable == null) {
+ // We shouldn't really get here as the background fallback should be always
+ // available since it is defaulted by the system.
+ Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
+ }
}
if (mCaptionBackgroundDrawable == null) {
mCaptionBackgroundDrawable = getContext().getDrawable(
@@ -1817,9 +1835,8 @@
* Returns the color used to fill areas the app has not rendered content to yet when the
* user is resizing the window of an activity in multi-window mode.
*/
- private Drawable getResizingBackgroundDrawable(int backgroundRes, int backgroundFallbackRes) {
- final Context context = getContext();
-
+ public static Drawable getResizingBackgroundDrawable(Context context, int backgroundRes,
+ int backgroundFallbackRes) {
if (backgroundRes != 0) {
final Drawable drawable = context.getDrawable(backgroundRes);
if (drawable != null) {
@@ -1833,10 +1850,6 @@
return fallbackDrawable;
}
}
-
- // We shouldn't really get here as the background fallback should be always available since
- // it is defaulted by the system.
- Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
return null;
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 6e374e2..08d4fba 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -17,6 +17,7 @@
package com.android.internal.statusbar;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
@@ -31,7 +32,23 @@
void animateExpandNotificationsPanel();
void animateExpandSettingsPanel(String subPanel);
void animateCollapsePanels();
- void setSystemUiVisibility(int vis, int mask);
+
+ /**
+ * Notifies the status bar of a System UI visibility flag change.
+ *
+ * @param vis the visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will be reported
+ * separately in fullscreenStackVis and dockedStackVis
+ * @param fullscreenStackVis the flags which only apply in the region of the fullscreen stack,
+ * which is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+ * @param dockedStackVis the flags that only apply in the region of the docked stack, which is
+ * currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+ * @param mask which flags to change
+ * @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates
+ * @param dockedBounds the current bounds of the docked stack, in screen coordinates
+ */
+ void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+ in Rect fullscreenBounds, in Rect dockedBounds);
+
void topAppWindowChanged(boolean menuVisible);
void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index bec18ec..8acf5d3 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -17,6 +17,7 @@
package com.android.internal.statusbar;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
@@ -37,7 +38,6 @@
void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription);
void setIconVisibility(String slot, boolean visible);
void removeIcon(String slot);
- void topAppWindowChanged(boolean menuVisible);
void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
void expandSettingsPanel(String subPanel);
@@ -47,7 +47,8 @@
// You need the STATUS_BAR_SERVICE permission
void registerStatusBar(IStatusBar callbacks, out List<String> iconSlots,
out List<StatusBarIcon> iconList,
- out int[] switches, out List<IBinder> binders);
+ out int[] switches, out List<IBinder> binders, out Rect fullscreenStackBounds,
+ out Rect dockedStackBounds);
void onPanelRevealed(boolean clearNotificationEffects, int numItems);
void onPanelHidden();
// Mark current notifications as "seen" and stop ringing, vibrating, blinking.
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e239852..cbc735f 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -283,6 +283,15 @@
getTrustManager().reportUnlockAttempt(true /* authenticated */, userId);
}
+ public int getCurrentFailedPasswordAttempts(int userId) {
+ return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
+ }
+
+ public int getMaximumFailedPasswordsForWipe(int userId) {
+ return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
+ null /* componentName */, userId);
+ }
+
/**
* Check to see if a pattern matches the saved pattern.
* If pattern matches, return an opaque attestation that the challenge
diff --git a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
index 0449340..1b40492 100644
--- a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
+++ b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
@@ -16,10 +16,6 @@
package com.android.server.backup;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.backup.BackupDataInputStream;
@@ -28,14 +24,21 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.SyncAdapterType;
+import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
+import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
@@ -73,6 +76,8 @@
private static final String KEY_AUTHORITY_NAME = "name";
private static final String KEY_AUTHORITY_SYNC_STATE = "syncState";
private static final String KEY_AUTHORITY_SYNC_ENABLED = "syncEnabled";
+ private static final String STASH_FILE = Environment.getDataDirectory()
+ + "/backup/unadded_account_syncsettings.json";
private Context mContext;
private AccountManager mAccountManager;
@@ -256,41 +261,99 @@
}
try {
- HashSet<Account> currentAccounts = getAccountsHashSet();
- for (int i = 0; i < accountJSONArray.length(); i++) {
- JSONObject accountJSON = (JSONObject) accountJSONArray.get(i);
- String accountName = accountJSON.getString(KEY_ACCOUNT_NAME);
- String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE);
-
- Account account = new Account(accountName, accountType);
-
- // Check if the account already exists. Accounts that don't exist on the device
- // yet won't be restored.
- if (currentAccounts.contains(account)) {
- restoreExistingAccountSyncSettingsFromJSON(accountJSON);
- } else {
- // TODO:
- // Stash the data to a file that the SyncManager can read from to restore
- // settings at a later date.
- }
- }
+ restoreFromJsonArray(accountJSONArray);
} finally {
// Set the master sync preference to the value from the backup set.
ContentResolver.setMasterSyncAutomatically(masterSyncEnabled);
}
-
Log.i(TAG, "Restore successful.");
} catch (IOException | JSONException e) {
Log.e(TAG, "Couldn't restore account sync settings\n" + e);
}
}
+ private void restoreFromJsonArray(JSONArray accountJSONArray)
+ throws JSONException {
+ HashSet<Account> currentAccounts = getAccounts();
+ JSONArray unaddedAccountsJSONArray = new JSONArray();
+ for (int i = 0; i < accountJSONArray.length(); i++) {
+ JSONObject accountJSON = (JSONObject) accountJSONArray.get(i);
+ String accountName = accountJSON.getString(KEY_ACCOUNT_NAME);
+ String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE);
+
+ Account account = null;
+ try {
+ account = new Account(accountName, accountType);
+ } catch (IllegalArgumentException iae) {
+ continue;
+ }
+
+ // Check if the account already exists. Accounts that don't exist on the device
+ // yet won't be restored.
+ if (currentAccounts.contains(account)) {
+ if (DEBUG) Log.i(TAG, "Restoring Sync Settings for" + accountName);
+ restoreExistingAccountSyncSettingsFromJSON(accountJSON);
+ } else {
+ unaddedAccountsJSONArray.put(accountJSON);
+ }
+ }
+
+ if (unaddedAccountsJSONArray.length() > 0) {
+ try (FileOutputStream fOutput = new FileOutputStream(STASH_FILE)) {
+ String jsonString = unaddedAccountsJSONArray.toString();
+ DataOutputStream out = new DataOutputStream(fOutput);
+ out.writeUTF(jsonString);
+ } catch (IOException ioe) {
+ // Error in writing to stash file
+ Log.e(TAG, "unable to write the sync settings to the stash file", ioe);
+ }
+ } else {
+ File stashFile = new File(STASH_FILE);
+ if (stashFile.exists()) stashFile.delete();
+ }
+ }
+
+ /**
+ * Restore SyncSettings for all existing accounts from a stashed backup-set
+ */
+ private void accountAddedInternal() {
+ String jsonString;
+
+ try (FileInputStream fIn = new FileInputStream(new File(STASH_FILE))) {
+ DataInputStream in = new DataInputStream(fIn);
+ jsonString = in.readUTF();
+ } catch (FileNotFoundException fnfe) {
+ // This is expected to happen when there is no accounts info stashed
+ if (DEBUG) Log.d(TAG, "unable to find the stash file", fnfe);
+ return;
+ } catch (IOException ioe) {
+ if (DEBUG) Log.d(TAG, "could not read sync settings from stash file", ioe);
+ return;
+ }
+
+ try {
+ JSONArray unaddedAccountsJSONArray = new JSONArray(jsonString);
+ restoreFromJsonArray(unaddedAccountsJSONArray);
+ } catch (JSONException jse) {
+ // Malformed jsonString
+ Log.e(TAG, "there was an error with the stashed sync settings", jse);
+ }
+ }
+
+ /**
+ * Restore SyncSettings for all existing accounts from a stashed backup-set
+ */
+ public static void accountAdded(Context context) {
+ AccountSyncSettingsBackupHelper helper = new AccountSyncSettingsBackupHelper(context);
+ helper.accountAddedInternal();
+ }
+
/**
* Helper method - fetch accounts and return them as a HashSet.
*
* @return Accounts in a HashSet.
*/
- private HashSet<Account> getAccountsHashSet() {
+ private HashSet<Account> getAccounts() {
Account[] accounts = mAccountManager.getAccounts();
HashSet<Account> accountHashSet = new HashSet<Account>();
for (Account account : accounts) {
@@ -359,4 +422,4 @@
public void writeNewStateDescription(ParcelFileDescriptor newState) {
}
-}
+}
\ No newline at end of file
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 92f7812..8b248b0 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -505,12 +505,6 @@
bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
}
-// Need to buffer enough input to be able to rewind as much as might be read by a decoder
-// trying to determine the stream's format. Currently the most is 64, read by
-// SkWebpCodec.
-// FIXME: Get this number from SkCodec
-#define BYTES_TO_BUFFER 64
-
static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
jobject padding, jobject options) {
@@ -519,7 +513,7 @@
if (stream.get()) {
std::unique_ptr<SkStreamRewindable> bufferedStream(
- SkFrontBufferedStream::Create(stream.release(), BYTES_TO_BUFFER));
+ SkFrontBufferedStream::Create(stream.release(), SkCodec::MinBufferedBytesNeeded()));
SkASSERT(bufferedStream.get() != NULL);
bitmap = doDecode(env, bufferedStream.release(), padding, options);
}
@@ -565,7 +559,7 @@
// ensures that SkImageDecoder::Factory never rewinds beyond the
// current position of the file descriptor.
std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream.release(),
- BYTES_TO_BUFFER));
+ SkCodec::MinBufferedBytesNeeded()));
return doDecode(env, stream.release(), padding, bitmapFactoryOptions);
}
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 7c8dbe8..2e974a3 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -20,11 +20,13 @@
#include <core_jni_helpers.h>
#include "SkData.h"
+#include "SkFontMgr.h"
#include "SkRefCnt.h"
#include "SkTypeface.h"
#include "GraphicsJNI.h"
#include <ScopedPrimitiveArray.h>
#include <ScopedUtfChars.h>
+#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_util_AssetManager.h>
#include <androidfw/AssetManager.h>
#include "Utils.h"
@@ -33,6 +35,8 @@
#include <minikin/FontFamily.h>
#include "MinikinSkia.h"
+#include <memory>
+
namespace android {
static jlong FontFamily_create(JNIEnv* env, jobject clazz, jstring lang, jint variant) {
@@ -69,13 +73,89 @@
return addSkTypeface(fontFamily, face);
}
+static struct {
+ jmethodID mGet;
+ jmethodID mSize;
+} gListClassInfo;
+
+static struct {
+ jfieldID mTag;
+ jfieldID mStyleValue;
+} gAxisClassInfo;
+
+static void release_global_ref(const void* /*data*/, void* context) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ bool needToAttach = (env == NULL);
+ if (needToAttach) {
+ JavaVMAttachArgs args;
+ args.version = JNI_VERSION_1_4;
+ args.name = "release_font_data";
+ args.group = NULL;
+ jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args);
+ if (result != JNI_OK) {
+ ALOGE("failed to attach to thread to release global ref.");
+ return;
+ }
+ }
+
+ jobject obj = reinterpret_cast<jobject>(context);
+ env->DeleteGlobalRef(obj);
+
+ if (needToAttach) {
+ AndroidRuntime::getJavaVM()->DetachCurrentThread();
+ }
+}
+
static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr,
- jstring path, jint ttcIndex, jint weight, jboolean isItalic) {
- NPE_CHECK_RETURN_ZERO(env, path);
- ScopedUtfChars str(env, path);
- SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
+ jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) {
+ NPE_CHECK_RETURN_ZERO(env, font);
+
+ // Declare axis native type.
+ std::unique_ptr<SkFontMgr::FontParameters::Axis[]> skiaAxes;
+ int skiaAxesLength = 0;
+ if (listOfAxis) {
+ jint listSize = env->CallIntMethod(listOfAxis, gListClassInfo.mSize);
+
+ skiaAxes.reset(new SkFontMgr::FontParameters::Axis[listSize]);
+ skiaAxesLength = listSize;
+ for (jint i = 0; i < listSize; ++i) {
+ jobject axisObject = env->CallObjectMethod(listOfAxis, gListClassInfo.mGet, i);
+ if (!axisObject) {
+ skiaAxes[i].fTag = 0;
+ skiaAxes[i].fStyleValue = 0;
+ continue;
+ }
+
+ jint tag = env->GetIntField(axisObject, gAxisClassInfo.mTag);
+ jfloat stylevalue = env->GetFloatField(axisObject, gAxisClassInfo.mStyleValue);
+ skiaAxes[i].fTag = tag;
+ skiaAxes[i].fStyleValue = SkFloatToScalar(stylevalue);
+ }
+ }
+
+ void* fontPtr = env->GetDirectBufferAddress(font);
+ if (fontPtr == NULL) {
+ ALOGE("addFont failed to create font, buffer invalid");
+ return false;
+ }
+ jlong fontSize = env->GetDirectBufferCapacity(font);
+ if (fontSize < 0) {
+ ALOGE("addFont failed to create font, buffer size invalid");
+ return false;
+ }
+ jobject fontRef = MakeGlobalRefOrDie(env, font);
+ SkAutoTUnref<SkData> data(SkData::NewWithProc(fontPtr, fontSize,
+ release_global_ref, reinterpret_cast<void*>(fontRef)));
+ std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data));
+
+ SkFontMgr::FontParameters params;
+ params.setCollectionIndex(ttcIndex);
+ params.setAxes(skiaAxes.get(), skiaAxesLength);
+
+ SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
+ SkTypeface* face = fm->createFromStream(fontData.release(), params);
if (face == NULL) {
- ALOGE("addFont failed to create font %s", str.c_str());
+ ALOGE("addFont failed to create font, invalid request");
return false;
}
FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
@@ -129,15 +209,26 @@
{ "nCreateFamily", "(Ljava/lang/String;I)J", (void*)FontFamily_create },
{ "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
{ "nAddFont", "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont },
- { "nAddFontWeightStyle", "(JLjava/lang/String;IIZ)Z", (void*)FontFamily_addFontWeightStyle },
+ { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z",
+ (void*)FontFamily_addFontWeightStyle },
{ "nAddFontFromAsset", "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
- (void*)FontFamily_addFontFromAsset },
+ (void*)FontFamily_addFontFromAsset },
};
int register_android_graphics_FontFamily(JNIEnv* env)
{
- return RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods,
- NELEM(gFontFamilyMethods));
+ int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods,
+ NELEM(gFontFamilyMethods));
+
+ jclass listClass = FindClassOrDie(env, "java/util/List");
+ gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
+ gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I");
+
+ jclass axisClass = FindClassOrDie(env, "android/graphics/FontListParser$Axis");
+ gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "tag", "I");
+ gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "styleValue", "F");
+
+ return err;
}
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 8e8f6c37..80f8a64 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -128,11 +128,12 @@
// other fields unused by JNI
} gAudioMixingRuleFields;
-static jclass gAttributeMatchCriterionClass;
+static jclass gAudioMixMatchCriterionClass;
static struct {
jfieldID mAttr;
+ jfieldID mIntProp;
jfieldID mRule;
-} gAttributeMatchCriterionFields;
+} gAudioMixMatchCriterionFields;
static jclass gAudioAttributesClass;
static struct {
@@ -1563,22 +1564,32 @@
}
for (jint i = 0; i < numCriteria; i++) {
- AttributeMatchCriterion nCriterion;
+ AudioMixMatchCriterion nCriterion;
jobject jCriterion = env->GetObjectArrayElement(jCriteria, i);
- nCriterion.mRule = env->GetIntField(jCriterion, gAttributeMatchCriterionFields.mRule);
+ nCriterion.mRule = env->GetIntField(jCriterion, gAudioMixMatchCriterionFields.mRule);
- jobject jAttributes = env->GetObjectField(jCriterion, gAttributeMatchCriterionFields.mAttr);
- if (nCriterion.mRule == RULE_MATCH_ATTRIBUTE_USAGE ||
- nCriterion.mRule == RULE_EXCLUDE_ATTRIBUTE_USAGE) {
- nCriterion.mAttr.mUsage = (audio_usage_t)env->GetIntField(jAttributes,
- gAudioAttributesFields.mUsage);
- } else {
- nCriterion.mAttr.mSource = (audio_source_t)env->GetIntField(jAttributes,
- gAudioAttributesFields.mSource);
+ const uint32_t match_rule = nCriterion.mRule & ~RULE_EXCLUSION_MASK;
+ switch (match_rule) {
+ case RULE_MATCH_UID:
+ nCriterion.mValue.mUid = env->GetIntField(jCriterion,
+ gAudioMixMatchCriterionFields.mIntProp);
+ break;
+ case RULE_MATCH_ATTRIBUTE_USAGE:
+ case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: {
+ jobject jAttributes = env->GetObjectField(jCriterion, gAudioMixMatchCriterionFields.mAttr);
+ if (match_rule == RULE_MATCH_ATTRIBUTE_USAGE) {
+ nCriterion.mValue.mUsage = (audio_usage_t)env->GetIntField(jAttributes,
+ gAudioAttributesFields.mUsage);
+ } else {
+ nCriterion.mValue.mSource = (audio_source_t)env->GetIntField(jAttributes,
+ gAudioAttributesFields.mSource);
+ }
+ env->DeleteLocalRef(jAttributes);
+ }
+ break;
}
- env->DeleteLocalRef(jAttributes);
nAudioMix->mCriteria.add(nCriterion);
env->DeleteLocalRef(jCriterion);
@@ -1833,12 +1844,14 @@
gAudioMixingRuleFields.mCriteria = GetFieldIDOrDie(env, audioMixingRuleClass, "mCriteria",
"Ljava/util/ArrayList;");
- jclass attributeMatchCriterionClass =
- FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule$AttributeMatchCriterion");
- gAttributeMatchCriterionClass = MakeGlobalRefOrDie(env, attributeMatchCriterionClass);
- gAttributeMatchCriterionFields.mAttr = GetFieldIDOrDie(env, attributeMatchCriterionClass, "mAttr",
+ jclass audioMixMatchCriterionClass =
+ FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion");
+ gAudioMixMatchCriterionClass = MakeGlobalRefOrDie(env,audioMixMatchCriterionClass);
+ gAudioMixMatchCriterionFields.mAttr = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mAttr",
"Landroid/media/AudioAttributes;");
- gAttributeMatchCriterionFields.mRule = GetFieldIDOrDie(env, attributeMatchCriterionClass, "mRule",
+ gAudioMixMatchCriterionFields.mIntProp = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mIntProp",
+ "I");
+ gAudioMixMatchCriterionFields.mRule = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mRule",
"I");
jclass audioAttributesClass = FindClassOrDie(env, "android/media/AudioAttributes");
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4e10d39..daa8202 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4204,11 +4204,16 @@
<string name="new_sms_notification_content">Open SMS app to view</string>
<!-- Notification title shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
- <string name="user_encrypted_title">Some functions might not be available</string>
+ <string name="user_encrypted_title">Some functionality may be limited</string>
<!-- Notification message shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
- <string name="user_encrypted_message">Touch to continue</string>
+ <string name="user_encrypted_message">Tap to unlock</string>
<!-- Notification detail shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
- <string name="user_encrypted_detail">User profile locked</string>
+ <string name="user_encrypted_detail">User data locked</string>
+
+ <!-- Notification detail shown when work profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
+ <string name="profile_encrypted_detail">Work profile locked</string>
+ <!-- Notification message shown when work profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
+ <string name="profile_encrypted_message">Tap to unlock work profile</string>
<!-- Title of notification shown after a MTP device is connected to Android. -->
<string name="usb_mtp_launch_notification_title">Connected to <xliff:g id="product_name">%1$s</xliff:g></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f75f023..8df6c2e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2522,6 +2522,8 @@
<java-symbol type="string" name="user_encrypted_title" />
<java-symbol type="string" name="user_encrypted_message" />
<java-symbol type="string" name="user_encrypted_detail" />
+ <java-symbol type="string" name="profile_encrypted_detail" />
+ <java-symbol type="string" name="profile_encrypted_message" />
<java-symbol type="drawable" name="ic_user_secure" />
<java-symbol type="string" name="usb_mtp_launch_notification_title" />
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 6309ed3..f741e3c 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -18,6 +18,9 @@
import android.content.res.AssetManager;
+import java.nio.ByteBuffer;
+import java.util.List;
+
/**
* A family of typefaces with different styles.
*
@@ -62,8 +65,9 @@
return nAddFont(mNativePtr, path, ttcIndex);
}
- public boolean addFontWeightStyle(String path, int ttcIndex, int weight, boolean style) {
- return nAddFontWeightStyle(mNativePtr, path, ttcIndex, weight, style);
+ public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List<FontListParser.Axis> axes,
+ int weight, boolean style) {
+ return nAddFontWeightStyle(mNativePtr, font, ttcIndex, axes, weight, style);
}
public boolean addFontFromAsset(AssetManager mgr, String path) {
@@ -73,8 +77,9 @@
private static native long nCreateFamily(String lang, int variant);
private static native void nUnrefFamily(long nativePtr);
private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex);
- private static native boolean nAddFontWeightStyle(long nativeFamily, String path,
- int ttcIndex, int weight, boolean isItalic);
+ private static native boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font,
+ int ttcIndex, List<FontListParser.Axis> listOfAxis,
+ int weight, boolean isItalic);
private static native boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr,
String path);
}
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 774f6b8..8f7c6a62 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -25,6 +25,7 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
+import java.util.regex.Pattern;
/**
* Parser for font config files.
@@ -42,15 +43,26 @@
public List<Alias> aliases;
}
+ public static class Axis {
+ Axis(int tag, float styleValue) {
+ this.tag = tag;
+ this.styleValue = styleValue;
+ }
+ public final int tag;
+ public final float styleValue;
+ }
+
public static class Font {
- Font(String fontName, int ttcIndex, int weight, boolean isItalic) {
+ Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic) {
this.fontName = fontName;
this.ttcIndex = ttcIndex;
+ this.axes = axes;
this.weight = weight;
this.isItalic = isItalic;
}
public String fontName;
public int ttcIndex;
+ public final List<Axis> axes;
public int weight;
public boolean isItalic;
}
@@ -93,9 +105,10 @@
parser.require(XmlPullParser.START_TAG, null, "familyset");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
- if (parser.getName().equals("family")) {
+ String tag = parser.getName();
+ if (tag.equals("family")) {
config.families.add(readFamily(parser));
- } else if (parser.getName().equals("alias")) {
+ } else if (tag.equals("alias")) {
config.aliases.add(readAlias(parser));
} else {
skip(parser);
@@ -114,14 +127,7 @@
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (tag.equals("font")) {
- String ttcIndexStr = parser.getAttributeValue(null, "index");
- int ttcIndex = ttcIndexStr == null ? 0 : Integer.parseInt(ttcIndexStr);
- String weightStr = parser.getAttributeValue(null, "weight");
- int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
- boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
- String filename = parser.nextText();
- String fullFilename = "/system/fonts/" + filename;
- fonts.add(new Font(fullFilename, ttcIndex, weight, isItalic));
+ fonts.add(readFont(parser));
} else {
skip(parser);
}
@@ -129,6 +135,70 @@
return new Family(name, fonts, lang, variant);
}
+ /** Matches leading and trailing XML whitespace. */
+ private static final Pattern FILENAME_WHITESPACE_PATTERN =
+ Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
+
+ private static Font readFont(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ String indexStr = parser.getAttributeValue(null, "index");
+ int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
+ List<Axis> axes = new ArrayList<Axis>();
+ String weightStr = parser.getAttributeValue(null, "weight");
+ int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
+ boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
+ StringBuilder filename = new StringBuilder();
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() == XmlPullParser.TEXT) {
+ filename.append(parser.getText());
+ }
+ if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+ String tag = parser.getName();
+ if (tag.equals("axis")) {
+ axes.add(readAxis(parser));
+ } else {
+ skip(parser);
+ }
+ }
+ String fullFilename = "/system/fonts/" +
+ FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
+ return new Font(fullFilename, index, axes, weight, isItalic);
+ }
+
+ /** The 'tag' attribute value is read as four character values between 0 and 255 inclusive. */
+ private static final Pattern TAG_PATTERN = Pattern.compile("[\\x00-\\xFF]{4}");
+
+ /** The 'styleValue' attribute has an optional leading '-', followed by '<digits>',
+ * '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9].
+ */
+ private static final Pattern STYLE_VALUE_PATTERN =
+ Pattern.compile("-?(([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))");
+
+ private static Axis readAxis(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int tag = 0;
+ String tagStr = parser.getAttributeValue(null, "tag");
+ if (tagStr != null && TAG_PATTERN.matcher(tagStr).matches()) {
+ tag = tagStr.charAt(0) << 24 +
+ tagStr.charAt(1) << 16 +
+ tagStr.charAt(2) << 8 +
+ tagStr.charAt(3);
+ } else {
+ throw new XmlPullParserException("Invalid tag attribute value.", parser, null);
+ }
+
+ float styleValue = 0;
+ String styleValueStr = parser.getAttributeValue(null, "stylevalue");
+ if (styleValueStr != null && STYLE_VALUE_PATTERN.matcher(styleValueStr).matches()) {
+ styleValue = Float.parseFloat(styleValueStr);
+ } else {
+ throw new XmlPullParserException("Invalid styleValue attribute value.", parser, null);
+ }
+
+ skip(parser); // axis tag is empty, ignore any contents and consume end tag
+ return new Axis(tag, styleValue);
+ }
+
private static Alias readAlias(XmlPullParser parser)
throws XmlPullParserException, IOException {
Alias alias = new Alias();
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 1294323..f15aff7 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -17,7 +17,6 @@
package android.graphics;
import android.content.res.AssetManager;
-import android.graphics.FontListParser.Family;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
@@ -28,6 +27,8 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -259,10 +260,26 @@
mStyle = nativeGetStyle(ni);
}
- private static FontFamily makeFamilyFromParsed(FontListParser.Family family) {
+ private static FontFamily makeFamilyFromParsed(FontListParser.Family family,
+ Map<String, ByteBuffer> bufferForPath) {
FontFamily fontFamily = new FontFamily(family.lang, family.variant);
for (FontListParser.Font font : family.fonts) {
- fontFamily.addFontWeightStyle(font.fontName, font.ttcIndex, font.weight, font.isItalic);
+ ByteBuffer fontBuffer = bufferForPath.get(font.fontName);
+ if (fontBuffer == null) {
+ try (FileInputStream file = new FileInputStream(font.fontName)) {
+ FileChannel fileChannel = file.getChannel();
+ long fontSize = fileChannel.size();
+ fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
+ bufferForPath.put(font.fontName, fontBuffer);
+ } catch (IOException e) {
+ Log.e(TAG, "Error mapping font file " + font.fontName);
+ continue;
+ }
+ }
+ if (!fontFamily.addFontWeightStyle(fontBuffer, font.ttcIndex, font.axes,
+ font.weight, font.isItalic)) {
+ Log.e(TAG, "Error creating font " + font.fontName + "#" + font.ttcIndex);
+ }
}
return fontFamily;
}
@@ -280,13 +297,15 @@
FileInputStream fontsIn = new FileInputStream(configFilename);
FontListParser.Config fontConfig = FontListParser.parse(fontsIn);
+ Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
+
List<FontFamily> familyList = new ArrayList<FontFamily>();
// Note that the default typeface is always present in the fallback list;
// this is an enhancement from pre-Minikin behavior.
for (int i = 0; i < fontConfig.families.size(); i++) {
- Family f = fontConfig.families.get(i);
+ FontListParser.Family f = fontConfig.families.get(i);
if (i == 0 || f.name == null) {
- familyList.add(makeFamilyFromParsed(f));
+ familyList.add(makeFamilyFromParsed(f, bufferForPath));
}
}
sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
@@ -295,14 +314,14 @@
Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
for (int i = 0; i < fontConfig.families.size(); i++) {
Typeface typeface;
- Family f = fontConfig.families.get(i);
+ FontListParser.Family f = fontConfig.families.get(i);
if (f.name != null) {
if (i == 0) {
// The first entry is the default typeface; no sense in
// duplicating the corresponding FontFamily.
typeface = sDefaultTypeface;
} else {
- FontFamily fontFamily = makeFamilyFromParsed(f);
+ FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
FontFamily[] families = { fontFamily };
typeface = Typeface.createFromFamiliesWithDefault(families);
}
@@ -324,11 +343,11 @@
Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
// TODO: normal in non-Minikin case, remove or make error when Minikin-only
} catch (FileNotFoundException e) {
- Log.e(TAG, "Error opening " + configFilename);
+ Log.e(TAG, "Error opening " + configFilename, e);
} catch (IOException e) {
- Log.e(TAG, "Error reading " + configFilename);
+ Log.e(TAG, "Error reading " + configFilename, e);
} catch (XmlPullParserException e) {
- Log.e(TAG, "XML parse exception for " + configFilename);
+ Log.e(TAG, "XML parse exception for " + configFilename, e);
}
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 1d9fe35..3277c36 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2236,7 +2236,7 @@
// See if any of the regions is better than the other
const int region_comparison = localeDataCompareRegions(
country, o.country,
- language, localeScript, requested->country);
+ language, requested->localeScript, requested->country);
if (region_comparison != 0) {
return (region_comparison > 0);
}
@@ -2526,17 +2526,34 @@
// For backward compatibility and supporting private-use locales, we
// fall back to old behavior if we couldn't determine the script for
- // either of the desired locale or the provided locale.
- if (localeScript[0] == '\0' || localeScript[1] == '\0') {
+ // either of the desired locale or the provided locale. But if we could determine
+ // the scripts, they should be the same for the locales to match.
+ bool countriesMustMatch = false;
+ char computed_script[4];
+ const char* script;
+ if (settings.localeScript[0] == '\0') { // could not determine the request's script
+ countriesMustMatch = true;
+ } else {
+ if (localeScript[0] == '\0') { // script was not provided, so we try to compute it
+ localeDataComputeScript(computed_script, language, country);
+ if (computed_script[0] == '\0') { // we could not compute the script
+ countriesMustMatch = true;
+ } else {
+ script = computed_script;
+ }
+ } else { // script was provided, so just use it
+ script = localeScript;
+ }
+ }
+
+ if (countriesMustMatch) {
if (country[0] != '\0'
&& (country[0] != settings.country[0]
|| country[1] != settings.country[1])) {
return false;
}
} else {
- // But if we could determine the scripts, they should be the same
- // for the locales to match.
- if (memcmp(localeScript, settings.localeScript, sizeof(localeScript)) != 0) {
+ if (memcmp(script, settings.localeScript, sizeof(settings.localeScript)) != 0) {
return false;
}
}
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 7b38640..4b8d65c 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -371,6 +371,19 @@
EXPECT_TRUE(supported.match(requested));
}
+TEST(ConfigLocaleTest, match_emptyScript) {
+ ResTable_config supported, requested;
+
+ fillIn("fr", "FR", NULL, NULL, &supported);
+ fillIn("fr", "CA", NULL, NULL, &requested);
+
+ // emulate packages built with older AAPT
+ memset(supported.localeScript, '\0', 4);
+ supported.localeScriptWasProvided = false;
+
+ EXPECT_TRUE(supported.match(requested));
+}
+
TEST(ConfigLocaleTest, isLocaleBetterThan_basics) {
ResTable_config config1, config2, request;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1f242a3..6a565033 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -144,20 +144,8 @@
external/skia/include/private \
external/skia/src/core
-hwui_shared_libraries := \
- liblog \
- libcutils \
- libutils \
- libEGL \
- libGLESv2 \
- libskia \
- libui \
- libgui \
- libprotobuf-cpp-lite \
-
ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
hwui_cflags += -DANDROID_ENABLE_RENDERSCRIPT
- hwui_shared_libraries += libRS libRScpp
hwui_c_includes += \
$(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) \
frameworks/rs/cpp \
@@ -180,12 +168,15 @@
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE := libhwui_static
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := $(hwui_cflags)
LOCAL_SRC_FILES := $(hwui_src_files)
LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH) \
+ $(hwui_c_includes) \
+ $(call hwui_proto_include)
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_STATIC_LIBRARY)
# ------------------------
@@ -196,7 +187,6 @@
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE := libhwui_static_null_gpu
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := \
$(hwui_cflags) \
-DHWUI_NULL_GPU
@@ -205,8 +195,12 @@
debug/nullegl.cpp \
debug/nullgles.cpp
LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH) \
+ $(hwui_c_includes) \
+ $(call hwui_proto_include)
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_STATIC_LIBRARY)
# ------------------------
@@ -218,8 +212,9 @@
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE := libhwui
LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_SHARED_LIBRARY)
# ------------------------
@@ -230,7 +225,6 @@
LOCAL_MODULE := hwui_unit_tests
LOCAL_MODULE_TAGS := tests
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu
LOCAL_CFLAGS := \
$(hwui_cflags) \
@@ -263,6 +257,7 @@
tests/unit/RecordingCanvasTests.cpp
endif
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_NATIVE_TEST)
# ------------------------
@@ -278,7 +273,6 @@
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := hwuitest
LOCAL_MODULE_STEM_64 := hwuitest64
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := $(hwui_cflags)
# set to libhwui_static_null_gpu to skip actual GL commands
@@ -289,6 +283,7 @@
tests/macrobench/TestSceneRunner.cpp \
tests/macrobench/main.cpp
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_EXECUTABLE)
# ------------------------
@@ -303,7 +298,6 @@
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := hwuimicro
LOCAL_MODULE_STEM_64 := hwuimicro64
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := \
$(hwui_cflags) \
-DHWUI_NULL_GPU
@@ -325,6 +319,5 @@
tests/microbench/FrameBuilderBench.cpp
endif
-LOCAL_CLANG := true # workaround gcc bug
-
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 11293d6..c8f5e94 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -165,6 +165,10 @@
generateTexture(colors, positions, info.width, 2, texture);
mSize += size;
+ LOG_ALWAYS_FATAL_IF((int)size != texture->objectSize(),
+ "size != texture->objectSize(), size %" PRIu32 ", objectSize %d"
+ " width = %" PRIu32 " bytesPerPixel() = %zu",
+ size, texture->objectSize(), info.width, bytesPerPixel());
mCache.put(gradient, texture);
return texture;
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index c09b6dd..4f49a35 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -28,13 +28,19 @@
static int bytesPerPixel(GLint glFormat) {
switch (glFormat) {
+ // The wrapped-texture case, usually means a SurfaceTexture
+ case 0:
+ return 0;
case GL_ALPHA:
return 1;
case GL_RGB:
return 3;
case GL_RGBA:
- default:
return 4;
+ case GL_RGBA16F:
+ return 16;
+ default:
+ LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat);
}
}
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
new file mode 100644
index 0000000..7d4ef0f
--- /dev/null
+++ b/libs/hwui/hwui_static_deps.mk
@@ -0,0 +1,28 @@
+###############################################################################
+#
+#
+# This file contains the shared and static dependencies needed by any target
+# that attempts to statically link HWUI (i.e. libhwui_static build target). This
+# file should be included by any target that lists libhwui_static as a
+# dependency.
+#
+# This is a workaround for the fact that the build system does not add these
+# transitive dependencies when it attempts to link libhwui_static into another
+# library.
+#
+###############################################################################
+
+LOCAL_SHARED_LIBRARIES += \
+ liblog \
+ libcutils \
+ libutils \
+ libEGL \
+ libGLESv2 \
+ libskia \
+ libui \
+ libgui \
+ libprotobuf-cpp-lite
+
+ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
+ LOCAL_SHARED_LIBRARIES += libRS libRScpp
+endif
\ No newline at end of file
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index d8f507c..a490685 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -28,7 +28,7 @@
*/
public final class GnssMeasurement implements Parcelable {
private int mFlags;
- private byte mPrn;
+ private short mSvid;
private double mTimeOffsetInNs;
private short mState;
private long mReceivedGpsTowInNs;
@@ -198,7 +198,7 @@
*/
public void set(GnssMeasurement measurement) {
mFlags = measurement.mFlags;
- mPrn = measurement.mPrn;
+ mSvid = measurement.mSvid;
mTimeOffsetInNs = measurement.mTimeOffsetInNs;
mState = measurement.mState;
mReceivedGpsTowInNs = measurement.mReceivedGpsTowInNs;
@@ -248,15 +248,15 @@
* Gets the Pseudo-random number (PRN).
* Range: [1, 32]
*/
- public byte getPrn() {
- return mPrn;
+ public short getSvid() {
+ return mSvid;
}
/**
* Sets the Pseud-random number (PRN).
*/
- public void setPrn(byte value) {
- mPrn = value;
+ public void setSvid(short value) {
+ mSvid = value;
}
/**
@@ -1210,7 +1210,7 @@
GnssMeasurement gnssMeasurement = new GnssMeasurement();
gnssMeasurement.mFlags = parcel.readInt();
- gnssMeasurement.mPrn = parcel.readByte();
+ gnssMeasurement.mSvid = (short) parcel.readInt();
gnssMeasurement.mTimeOffsetInNs = parcel.readDouble();
gnssMeasurement.mState = (short) parcel.readInt();
gnssMeasurement.mReceivedGpsTowInNs = parcel.readLong();
@@ -1253,9 +1253,10 @@
}
};
+ @Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mFlags);
- parcel.writeByte(mPrn);
+ parcel.writeInt(mSvid);
parcel.writeDouble(mTimeOffsetInNs);
parcel.writeInt(mState);
parcel.writeLong(mReceivedGpsTowInNs);
@@ -1301,7 +1302,7 @@
final String formatWithUncertainty = " %-29s = %-25s %-40s = %s\n";
StringBuilder builder = new StringBuilder("GnssMeasurement:\n");
- builder.append(String.format(format, "Prn", mPrn));
+ builder.append(String.format(format, "Svid", mSvid));
builder.append(String.format(format, "TimeOffsetInNs", mTimeOffsetInNs));
@@ -1422,7 +1423,7 @@
private void initialize() {
mFlags = HAS_NO_FLAGS;
- setPrn(Byte.MIN_VALUE);
+ setSvid((short) 0);
setTimeOffsetInNs(Long.MIN_VALUE);
setState(STATE_UNKNOWN);
setReceivedGpsTowInNs(Long.MIN_VALUE);
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index 0e011d5..86328eb 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -26,7 +26,7 @@
import java.security.InvalidParameterException;
/**
- * A class containing a GPS satellite Navigation Message.
+ * A class containing a GNSS satellite Navigation Message.
*/
public final class GnssNavigationMessage implements Parcelable {
@@ -84,7 +84,7 @@
// End enumerations in sync with gps.h
private byte mType;
- private byte mPrn;
+ private short mSvid;
private short mMessageId;
private short mSubmessageId;
private byte[] mData;
@@ -99,7 +99,7 @@
*/
public void set(GnssNavigationMessage navigationMessage) {
mType = navigationMessage.mType;
- mPrn = navigationMessage.mPrn;
+ mSvid = navigationMessage.mSvid;
mMessageId = navigationMessage.mMessageId;
mSubmessageId = navigationMessage.mSubmessageId;
mData = navigationMessage.mData;
@@ -153,15 +153,15 @@
* Gets the Pseudo-random number.
* Range: [1, 32].
*/
- public byte getPrn() {
- return mPrn;
+ public short getSvid() {
+ return mSvid;
}
/**
* Sets the Pseud-random number.
*/
- public void setPrn(byte value) {
- mPrn = value;
+ public void setSvid(short value) {
+ mSvid = value;
}
/**
@@ -256,7 +256,7 @@
GnssNavigationMessage navigationMessage = new GnssNavigationMessage();
navigationMessage.setType(parcel.readByte());
- navigationMessage.setPrn(parcel.readByte());
+ navigationMessage.setSvid((short) parcel.readInt());
navigationMessage.setMessageId((short) parcel.readInt());
navigationMessage.setSubmessageId((short) parcel.readInt());
@@ -281,9 +281,10 @@
}
};
+ @Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeByte(mType);
- parcel.writeByte(mPrn);
+ parcel.writeInt(mSvid);
parcel.writeInt(mMessageId);
parcel.writeInt(mSubmessageId);
parcel.writeInt(mData.length);
@@ -302,7 +303,7 @@
StringBuilder builder = new StringBuilder("GnssNavigationMessage:\n");
builder.append(String.format(format, "Type", getTypeString()));
- builder.append(String.format(format, "Prn", mPrn));
+ builder.append(String.format(format, "Svid", mSvid));
builder.append(String.format(format, "Status", getStatusString()));
builder.append(String.format(format, "MessageId", mMessageId));
builder.append(String.format(format, "SubmessageId", mSubmessageId));
@@ -321,7 +322,7 @@
private void initialize() {
mType = MESSAGE_TYPE_UNKNOWN;
- mPrn = 0;
+ mSvid = 0;
mMessageId = -1;
mSubmessageId = -1;
mData = EMPTY_ARRAY;
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 77e8a5b..906e944 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -47,24 +47,26 @@
public static final int GNSS_SV_FLAGS_USED_IN_FIX = (1 << 2);
/** @hide */
- public static final int PRN_SHIFT_WIDTH = 3;
+ public static final int SVID_SHIFT_WIDTH = 7;
+ /** @hide */
+ public static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 3;
+ /** @hide */
+ public static final int CONSTELLATION_TYPE_MASK = 0xf;
/* These package private values are modified by the LocationManager class */
- /* package */ int[] mPrnWithFlags;
+ /* package */ int[] mSvidWithFlags;
/* package */ float[] mSnrs;
/* package */ float[] mElevations;
/* package */ float[] mAzimuths;
- /* package */ int[] mConstellationTypes;
/* package */ int mSvCount;
- GnssStatus(int svCount, int[] prnWithFlags, float[] snrs, float[] elevations, float[] azimuths,
- int[] constellationTypes) {
+ GnssStatus(int svCount, int[] svidWithFlags, float[] snrs, float[] elevations,
+ float[] azimuths) {
mSvCount = svCount;
- mPrnWithFlags = prnWithFlags;
+ mSvidWithFlags = svidWithFlags;
mSnrs = snrs;
mElevations = elevations;
mAzimuths = azimuths;
- mConstellationTypes = constellationTypes;
}
/**
@@ -79,15 +81,16 @@
* @param satIndex the index of the satellite in the list.
*/
public int getConstellationType(int satIndex) {
- return mConstellationTypes[satIndex];
+ return (mSvidWithFlags[satIndex] >> CONSTELLATION_TYPE_SHIFT_WIDTH)
+ & CONSTELLATION_TYPE_MASK;
}
/**
* Retrieves the pseudo-random number of the satellite at the specified position.
* @param satIndex the index of the satellite in the list.
*/
- public int getPrn(int satIndex) {
- return mPrnWithFlags[satIndex] >> PRN_SHIFT_WIDTH;
+ public int getSvid(int satIndex) {
+ return mSvidWithFlags[satIndex] >> SVID_SHIFT_WIDTH;
}
/**
@@ -119,7 +122,7 @@
* @param satIndex the index of the satellite in the list.
*/
public boolean hasEphemeris(int satIndex) {
- return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
}
/**
@@ -127,7 +130,7 @@
* @param satIndex the index of the satellite in the list.
*/
public boolean hasAlmanac(int satIndex) {
- return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
}
/**
@@ -135,6 +138,6 @@
* @param satIndex the index of the satellite in the list.
*/
public boolean usedInFix(int satIndex) {
- return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
}
}
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 8d2f781..e41e20c 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -138,15 +138,19 @@
// For API-compat a public ctor() is not available
GpsStatus() {}
- private void setStatus(int svCount, int[] prnWithFlags, float[] snrs, float[] elevations,
- float[] azimuths, int[] constellationTypes) {
+ private void setStatus(int svCount, int[] svidWithFlags, float[] snrs, float[] elevations,
+ float[] azimuths) {
clearSatellites();
for (int i = 0; i < svCount; i++) {
+ final int constellationType =
+ (svidWithFlags[i] >> GnssStatus.CONSTELLATION_TYPE_SHIFT_WIDTH)
+ & GnssStatus.CONSTELLATION_TYPE_MASK;
// Skip all non-GPS satellites.
- if (constellationTypes[i] != GnssStatus.CONSTELLATION_GPS) {
+ if (constellationType != GnssStatus.CONSTELLATION_GPS) {
+ // TODO: translate the defacto pre-N use of prn's >32 to new struct
continue;
}
- int prn = prnWithFlags[i] >> GnssStatus.PRN_SHIFT_WIDTH;
+ int prn = svidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH;
if (prn > 0 && prn <= NUM_SATELLITES) {
GpsSatellite satellite = mSatellites.get(prn);
if (satellite == null) {
@@ -159,11 +163,11 @@
satellite.mElevation = elevations[i];
satellite.mAzimuth = azimuths[i];
satellite.mHasEphemeris =
- (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
satellite.mHasAlmanac =
- (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
satellite.mUsedInFix =
- (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0;
}
}
}
@@ -176,8 +180,8 @@
*/
void setStatus(GnssStatus status, int timeToFirstFix) {
mTimeToFirstFix = timeToFirstFix;
- setStatus(status.mSvCount, status.mPrnWithFlags, status.mSnrs, status.mElevations,
- status.mAzimuths, status.mConstellationTypes);
+ setStatus(status.mSvCount, status.mSvidWithFlags, status.mSnrs, status.mElevations,
+ status.mAzimuths);
}
void setTimeToFirstFix(int ttff) {
diff --git a/location/java/android/location/IGnssStatusListener.aidl b/location/java/android/location/IGnssStatusListener.aidl
index d1c6a85..8c7d06e 100644
--- a/location/java/android/location/IGnssStatusListener.aidl
+++ b/location/java/android/location/IGnssStatusListener.aidl
@@ -26,7 +26,7 @@
void onGnssStarted();
void onGnssStopped();
void onFirstFix(int ttff);
- void onSvStatusChanged(int svCount, in int[] prnWithFlags, in float[] snrs,
- in float[] elevations, in float[] azimuths, in int[] constellationTypes);
+ void onSvStatusChanged(int svCount, in int[] svidWithFlags, in float[] snrs,
+ in float[] elevations, in float[] azimuths);
void onNmeaReceived(long timestamp, String nmea);
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 30cf101..23f0710 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1562,10 +1562,9 @@
@Override
public void onSvStatusChanged(int svCount, int[] prnWithFlags,
- float[] snrs, float[] elevations, float[] azimuths, int[] constellationTypes) {
+ float[] snrs, float[] elevations, float[] azimuths) {
if (mGnssCallback != null) {
- mGnssStatus = new GnssStatus(svCount, prnWithFlags, snrs, elevations, azimuths,
- constellationTypes);
+ mGnssStatus = new GnssStatus(svCount, prnWithFlags, snrs, elevations, azimuths);
Message msg = Message.obtain();
msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index e0fa592f..6fc2f87 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1052,32 +1052,27 @@
}
}
-// TODO Change getBufferCapacityInFrames() reference below to
-// {@link #getBufferCapacityInFrames()} after @hide is removed.
-// TODO Change setBufferSizeInFrames(int) reference below to
-// {@link #setBufferSizeInFrames(int)} after @hide is removed.
+
/**
- * Returns the effective size of the <code>AudioTrack</code> buffer
+ * Returns the effective size of the <code>AudioTrack</code> buffer
* that the application writes to.
- * <p> This will be less than or equal to the result of
- * getBufferCapacityInFrames().
- * It will be equal if setBufferSizeInFrames(int) has never been called.
- * <p> If the track is subsequently routed to a different output sink, the buffer
- * size and capacity may enlarge to accommodate.
- * <p> If the <code>AudioTrack</code> encoding indicates compressed data,
- * e.g. {@link AudioFormat#ENCODING_AC3}, then the frame count returned is
- * the size of the native <code>AudioTrack</code> buffer in bytes.
- * <p> See also {@link AudioManager#getProperty(String)} for key
- * {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
- * @return current size in frames of the <code>AudioTrack</code> buffer.
- * @throws IllegalStateException
+ * <p> This will be less than or equal to the result of
+ * {@link #getBufferCapacityInFrames()}.
+ * It will be equal if {@link #setBufferSizeInFrames(int)} has never been called.
+ * <p> If the track is subsequently routed to a different output sink, the buffer
+ * size and capacity may enlarge to accommodate.
+ * <p> If the <code>AudioTrack</code> encoding indicates compressed data,
+ * e.g. {@link AudioFormat#ENCODING_AC3}, then the frame count returned is
+ * the size of the native <code>AudioTrack</code> buffer in bytes.
+ * <p> See also {@link AudioManager#getProperty(String)} for key
+ * {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
+ * @return current size in frames of the <code>AudioTrack</code> buffer.
+ * @throws IllegalStateException
*/
public int getBufferSizeInFrames() {
return native_get_buffer_size_frames();
}
-// TODO Change getBufferCapacityInFrames() reference below to
-// {@link #getBufferCapacityInFrames()} after @hide is removed.
/**
* Limits the effective size of the <code>AudioTrack</code> buffer
* that the application writes to.
@@ -1087,9 +1082,9 @@
* <p>Changing this limit modifies the latency associated with
* the buffer for this track. A smaller size will give lower latency
* but there may be more glitches due to buffer underruns.
- * <p>The actual size used may not be equal to this requested size.
+ * <p>The actual size used may not be equal to this requested size.
* It will be limited to a valid range with a maximum of
- * getBufferCapacityInFrames().
+ * {@link #getBufferCapacityInFrames()}.
* It may also be adjusted slightly for internal reasons.
* If bufferSizeInFrames is less than zero then {@link #ERROR_BAD_VALUE}
* will be returned.
@@ -1097,9 +1092,9 @@
* It is not supported for compressed audio tracks.
*
* @param bufferSizeInFrames requested buffer size
- * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
- * {@link #ERROR_INVALID_OPERATION}
- * @throws IllegalStateException
+ * @return the actual buffer size in frames or an error code,
+ * {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
+ * @throws IllegalStateException
*/
public int setBufferSizeInFrames(int bufferSizeInFrames) {
if (mDataLoadMode == MODE_STATIC || mState == STATE_UNINITIALIZED) {
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index 54543ec..e197141 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -42,7 +42,7 @@
@SystemApi
public class AudioMixingRule {
- private AudioMixingRule(int mixType, ArrayList<AttributeMatchCriterion> criteria) {
+ private AudioMixingRule(int mixType, ArrayList<AudioMixMatchCriterion> criteria) {
mCriteria = criteria;
mTargetMixType = mixType;
}
@@ -91,21 +91,21 @@
public static final int RULE_EXCLUDE_UID =
RULE_EXCLUSION_MASK | RULE_MATCH_UID;
- static final class AttributeMatchCriterion {
+ static final class AudioMixMatchCriterion {
final AudioAttributes mAttr;
- final Integer mIntProp;
+ final int mIntProp;
final int mRule;
/** input parameters must be valid */
- AttributeMatchCriterion(AudioAttributes attributes, int rule) {
+ AudioMixMatchCriterion(AudioAttributes attributes, int rule) {
mAttr = attributes;
- mIntProp = null;
+ mIntProp = Integer.MIN_VALUE;
mRule = rule;
}
/** input parameters must be valid */
- AttributeMatchCriterion(Integer intProp, int rule) {
+ AudioMixMatchCriterion(Integer intProp, int rule) {
mAttr = null;
- mIntProp = intProp;
+ mIntProp = intProp.intValue();
mRule = rule;
}
@@ -125,10 +125,10 @@
dest.writeInt(mAttr.getCapturePreset());
break;
case RULE_MATCH_UID:
- dest.writeInt(mIntProp.intValue());
+ dest.writeInt(mIntProp);
break;
default:
- Log.e("AttributeMatchCriterion", "Unknown match rule" + match_rule
+ Log.e("AudioMixMatchCriterion", "Unknown match rule" + match_rule
+ " when writing to Parcel");
dest.writeInt(-1);
}
@@ -137,8 +137,8 @@
private final int mTargetMixType;
int getTargetMixType() { return mTargetMixType; }
- private final ArrayList<AttributeMatchCriterion> mCriteria;
- ArrayList<AttributeMatchCriterion> getCriteria() { return mCriteria; }
+ private final ArrayList<AudioMixMatchCriterion> mCriteria;
+ ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; }
@Override
public int hashCode() {
@@ -205,7 +205,7 @@
*/
@SystemApi
public static class Builder {
- private ArrayList<AttributeMatchCriterion> mCriteria;
+ private ArrayList<AudioMixMatchCriterion> mCriteria;
private int mTargetMixType = AudioMix.MIX_TYPE_INVALID;
/**
@@ -213,7 +213,7 @@
*/
@SystemApi
public Builder() {
- mCriteria = new ArrayList<AttributeMatchCriterion>();
+ mCriteria = new ArrayList<AudioMixMatchCriterion>();
}
/**
@@ -378,10 +378,10 @@
throw new IllegalArgumentException("Incompatible rule for mix");
}
synchronized (mCriteria) {
- Iterator<AttributeMatchCriterion> crIterator = mCriteria.iterator();
+ Iterator<AudioMixMatchCriterion> crIterator = mCriteria.iterator();
final int match_rule = rule & ~RULE_EXCLUSION_MASK;
while (crIterator.hasNext()) {
- final AttributeMatchCriterion criterion = crIterator.next();
+ final AudioMixMatchCriterion criterion = crIterator.next();
switch (match_rule) {
case RULE_MATCH_ATTRIBUTE_USAGE:
// "usage"-based rule
@@ -413,7 +413,7 @@
break;
case RULE_MATCH_UID:
// "usage"-based rule
- if (criterion.mIntProp.intValue() == intProp.intValue()) {
+ if (criterion.mIntProp == intProp.intValue()) {
if (criterion.mRule == rule) {
// rule already exists, we're done
return this;
@@ -431,10 +431,10 @@
switch (match_rule) {
case RULE_MATCH_ATTRIBUTE_USAGE:
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
- mCriteria.add(new AttributeMatchCriterion(attrToMatch, rule));
+ mCriteria.add(new AudioMixMatchCriterion(attrToMatch, rule));
break;
case RULE_MATCH_UID:
- mCriteria.add(new AttributeMatchCriterion(intProp, rule));
+ mCriteria.add(new AudioMixMatchCriterion(intProp, rule));
break;
default:
throw new IllegalStateException("Unreachable code in addRuleInternal()");
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index 5ad6126..5d2bac0 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -17,7 +17,7 @@
package android.media.audiopolicy;
import android.media.AudioFormat;
-import android.media.audiopolicy.AudioMixingRule.AttributeMatchCriterion;
+import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -86,9 +86,9 @@
dest.writeInt(mix.getFormat().getEncoding());
dest.writeInt(mix.getFormat().getChannelMask());
// write mix rules
- final ArrayList<AttributeMatchCriterion> criteria = mix.getRule().getCriteria();
+ final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
dest.writeInt(criteria.size());
- for (AttributeMatchCriterion criterion : criteria) {
+ for (AudioMixMatchCriterion criterion : criteria) {
criterion.writeToParcel(dest);
}
}
@@ -150,8 +150,8 @@
textDump += " channels=0x";
textDump += Integer.toHexString(mix.getFormat().getChannelMask()).toUpperCase() +"\n";
// write mix rules
- final ArrayList<AttributeMatchCriterion> criteria = mix.getRule().getCriteria();
- for (AttributeMatchCriterion criterion : criteria) {
+ final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
+ for (AudioMixMatchCriterion criterion : criteria) {
switch(criterion.mRule) {
case AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_USAGE:
textDump += " exclude usage ";
@@ -171,11 +171,11 @@
break;
case AudioMixingRule.RULE_MATCH_UID:
textDump += " match UID ";
- textDump += criterion.mIntProp.toString();
+ textDump += criterion.mIntProp;
break;
case AudioMixingRule.RULE_EXCLUDE_UID:
textDump += " exclude UID ";
- textDump += criterion.mIntProp.toString();
+ textDump += criterion.mIntProp;
break;
default:
textDump += "invalid rule!";
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 1320e38..00083e6 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -157,35 +157,22 @@
public static final int RECORDING_ERROR_UNKNOWN = 0;
/**
- * Error for {@link TvRecordingClient.RecordingCallback#onError(int)}: The recording client has
- * failed to establish a connection to a recording session.
- */
- public static final int RECORDING_ERROR_CONNECTION_FAILED = 1;
-
- /**
- * Error for {@link TvRecordingClient.RecordingCallback#onError(int)}: The recording client has
- * been disconnected from the current recording session.
- */
- public static final int RECORDING_ERROR_DISCONNECTED = 2;
-
- /**
* Error for {@link TvInputService.RecordingSession#notifyError(int)} and
* {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed due to
* insufficient storage space.
*/
- public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3;
+ public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1;
/**
* Error for {@link TvInputService.RecordingSession#notifyError(int)} and
* {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed because
* a required recording resource was not able to be allocated.
*/
- public static final int RECORDING_ERROR_RESOURCE_BUSY = 4;
+ public static final int RECORDING_ERROR_RESOURCE_BUSY = 2;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_CONNECTION_FAILED,
- RECORDING_ERROR_DISCONNECTED, RECORDING_ERROR_INSUFFICIENT_SPACE,
+ @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_INSUFFICIENT_SPACE,
RECORDING_ERROR_RESOURCE_BUSY})
public @interface RecordingError {}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 334c84b..4ebd0fc 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1584,6 +1584,10 @@
* new data entry in the {@link TvContract.RecordedPrograms} table that describes the newly
* recorded program.
*
+ * <p>The recording session must call this method in response to {@link #onStopRecording()}.
+ * The session may call it even before receiving a call to {@link #onStopRecording()} if a
+ * partially recorded program is available when there is an error.
+ *
* @param recordedProgramUri The URI of the newly recorded program.
*/
public void notifyRecordingStopped(final Uri recordedProgramUri) {
@@ -1604,8 +1608,14 @@
}
/**
- * Informs the application that there is an error. It may be called at any time after this
- * recording session is created until {@link #onRelease()} is called.
+ * Informs the application that there is an error and this recording session is no longer
+ * able to start or continue recording. It may be called at any time after the recording
+ * session is created until {@link #onRelease()} is called.
+ *
+ * <p>The application may release the current session upon receiving the error code through
+ * {@link TvRecordingClient.RecordingCallback#onError(int)}. The session may call
+ * {@link #notifyRecordingStopped(Uri)} if a partially recorded but still playable program
+ * is available, before calling this method.
*
* @param error The error code. Should be one of the followings.
* <ul>
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 72606f5..1c920f5 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -68,7 +68,7 @@
/**
* Tunes to a given channel for TV program recording. The first tune request will create a new
- * recording session for the corresponding TV input and establish the connection between the
+ * recording session for the corresponding TV input and establish a connection between the
* application and the session. If recording has already started in the current recording
* session, this method throws an exception.
*
@@ -88,7 +88,7 @@
/**
* Tunes to a given channel for TV program recording. The first tune request will create a new
- * recording session for the corresponding TV input and establish the connection between the
+ * recording session for the corresponding TV input and establish a connection between the
* application and the session. If recording has already started in the current recording
* session, this method throws an exception.
*
@@ -226,6 +226,23 @@
*/
public abstract static class RecordingCallback {
/**
+ * This is called when an error occurred while establishing a connection to the recording
+ * session for the corresponding TV input.
+ *
+ * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+ */
+ public void onConnectionFailed(String inputId) {
+ }
+
+ /**
+ * This is called when the connection to the current recording session is lost.
+ *
+ * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+ */
+ public void onDisconnected(String inputId) {
+ }
+
+ /**
* This is called when the recording session has been tuned to the given channel and is
* ready to start recording.
*/
@@ -249,8 +266,6 @@
* @param error The error code. Should be one of the followings.
* <ul>
* <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
- * <li>{@link TvInputManager#RECORDING_ERROR_CONNECTION_FAILED}
- * <li>{@link TvInputManager#RECORDING_ERROR_DISCONNECTED}
* <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
* <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
* </ul>
@@ -305,7 +320,9 @@
mSession.tune(mChannelUri, mConnectionParams);
} else {
mSessionCallback = null;
- mCallback.onError(TvInputManager.RECORDING_ERROR_CONNECTION_FAILED);
+ if (mCallback != null) {
+ mCallback.onConnectionFailed(mInputId);
+ }
}
}
@@ -331,11 +348,13 @@
Log.w(TAG, "onSessionReleased - session not created");
return;
}
- mSessionCallback = null;
- mSession = null;
mIsTuned = false;
mIsRecordingStarted = false;
- mCallback.onError(TvInputManager.RECORDING_ERROR_DISCONNECTED);
+ mSessionCallback = null;
+ mSession = null;
+ if (mCallback != null) {
+ mCallback.onDisconnected(mInputId);
+ }
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
index ff1940a..4cba135 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
@@ -132,7 +132,7 @@
showBreadcrumb(true);
mToolbar.setTitle(null);
mIgnoreNextNavigation = true;
- mBreadcrumb.setSelection(mBreadcrumbAdapter.getCount() - 1);
+ mBreadcrumb.setSelection(mBreadcrumbAdapter.getCount() - 1, false);
}
if (DEBUG) Log.d(TAG, "Final toolbar title is: " + mToolbar.getTitle());
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 726538e..f8735b2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -101,7 +101,6 @@
import com.android.documentsui.services.FileOperationService;
import com.android.documentsui.services.FileOperationService.OpType;
import com.android.documentsui.services.FileOperations;
-
import com.google.common.collect.Lists;
import java.lang.annotation.Retention;
@@ -194,6 +193,11 @@
mRecView.setItemAnimator(new DirectoryItemAnimator(getActivity()));
+ // Make the RecyclerView unfocusable. This is needed in order for the focus search code in
+ // FocusManager to work correctly. Setting android:focusable=false in the layout xml doesn't
+ // work, for some reason.
+ mRecView.setFocusable(false);
+
// TODO: Add a divider between views (which might use RecyclerView.ItemDecoration).
if (DEBUG_ENABLE_DND) {
setupDragAndDropOnDirectoryView(mRecView);
@@ -264,7 +268,7 @@
mSelectionManager.addCallback(selectionListener);
// Make sure this is done after the RecyclerView is set up.
- mFocusManager = new FocusManager(mRecView, mSelectionManager);
+ mFocusManager = new FocusManager(mRecView);
mModel = new Model();
mModel.addUpdateListener(mAdapter);
@@ -1262,16 +1266,37 @@
}
if (mFocusManager.handleKey(doc, keyCode, event)) {
+ // Handle range selection adjustments. Extending the selection will adjust the
+ // bounds of the in-progress range selection. Each time an unshifted navigation
+ // event is received, the range selection is restarted.
+ if (shouldExtendSelection(event)) {
+ if (!mSelectionManager.isRangeSelectionActive()) {
+ // Start a range selection if one isn't active
+ mSelectionManager.startRangeSelection(doc.getAdapterPosition());
+ }
+ mSelectionManager.snapRangeSelection(mFocusManager.getFocusPosition());
+ } else {
+ mSelectionManager.endRangeSelection();
+ }
return true;
}
// Handle enter key events
if (keyCode == KeyEvent.KEYCODE_ENTER) {
- return onActivate(doc);
+ if (event.isShiftPressed()) {
+ return onSelect(doc);
+ } else {
+ return onActivate(doc);
+ }
}
return false;
}
+
+ private boolean shouldExtendSelection(KeyEvent event) {
+ return Events.isNavigationKeyCode(event.getKeyCode()) &&
+ event.isShiftPressed();
+ }
}
private final class ModelUpdateListener implements Model.UpdateListener {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
index ad010a6..93ec842 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
@@ -16,7 +16,7 @@
package com.android.documentsui.dirlist;
-import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.KeyEvent;
@@ -32,16 +32,14 @@
private RecyclerView mView;
private RecyclerView.Adapter<?> mAdapter;
- private LinearLayoutManager mLayout;
- private MultiSelectManager mSelectionManager;
+ private GridLayoutManager mLayout;
private int mLastFocusPosition = RecyclerView.NO_POSITION;
- public FocusManager(RecyclerView view, MultiSelectManager selectionManager) {
+ public FocusManager(RecyclerView view) {
mView = view;
mAdapter = view.getAdapter();
- mLayout = (LinearLayoutManager) view.getLayoutManager();
- mSelectionManager = selectionManager;
+ mLayout = (GridLayoutManager) view.getLayoutManager();
}
/**
@@ -54,19 +52,24 @@
* @return Whether the event was handled.
*/
public boolean handleKey(DocumentHolder doc, int keyCode, KeyEvent event) {
+ boolean extendSelection = false;
+ // Translate space/shift-space into PgDn/PgUp
+ if (keyCode == KeyEvent.KEYCODE_SPACE) {
+ if (event.isShiftPressed()) {
+ keyCode = KeyEvent.KEYCODE_PAGE_UP;
+ } else {
+ keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
+ }
+ } else {
+ extendSelection = event.isShiftPressed();
+ }
+
if (Events.isNavigationKeyCode(keyCode)) {
// Find the target item and focus it.
int endPos = findTargetPosition(doc.itemView, keyCode, event);
if (endPos != RecyclerView.NO_POSITION) {
focusItem(endPos);
- boolean extendSelection = event.isShiftPressed();
-
- // Handle any necessary adjustments to selection.
- if (extendSelection) {
- int startPos = doc.getAdapterPosition();
- mSelectionManager.selectRange(startPos, endPos);
- }
}
// Swallow all navigation keystrokes. Otherwise they go to the app's global
// key-handler, which will route them back to the DF and cause focus to be reset.
@@ -97,6 +100,13 @@
}
/**
+ * @return The adapter position of the last focused item.
+ */
+ public int getFocusPosition() {
+ return mLastFocusPosition;
+ }
+
+ /**
* Finds the destination position where the focus should land for a given navigation event.
*
* @param view The view that received the event.
@@ -124,12 +134,27 @@
case KeyEvent.KEYCODE_DPAD_DOWN:
searchDir = View.FOCUS_DOWN;
break;
- case KeyEvent.KEYCODE_DPAD_LEFT:
- searchDir = View.FOCUS_LEFT;
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- searchDir = View.FOCUS_RIGHT;
- break;
+ }
+
+ if (inGridMode()) {
+ int currentPosition = mView.getChildAdapterPosition(view);
+ // Left and right arrow keys only work in grid mode.
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (currentPosition > 0) {
+ // Stop backward focus search at the first item, otherwise focus will wrap
+ // around to the last visible item.
+ searchDir = View.FOCUS_BACKWARD;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (currentPosition < mAdapter.getItemCount() - 1) {
+ // Stop forward focus search at the last item, otherwise focus will wrap
+ // around to the first visible item.
+ searchDir = View.FOCUS_FORWARD;
+ }
+ break;
+ }
}
if (searchDir != -1) {
@@ -228,4 +253,11 @@
});
}
}
+
+ /**
+ * @return Whether the layout manager is currently in a grid-configuration.
+ */
+ private boolean inGridMode() {
+ return mLayout.getSpanCount() > 1;
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index d60825b..c8b6f85 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -370,39 +370,41 @@
}
/**
- * Handle a range selection event.
- * <li> If the MSM is currently in single-select mode, only the last item in the range will
- * actually be selected.
- * <li>If a range selection is not already active, one will be started, and the given range of
- * items will be selected. The given startPos becomes the anchor for the range selection.
- * <li>If a range selection is already active, the anchor is not changed. The range is extended
- * from its current anchor to endPos.
+ * Starts a range selection. If a range selection is already active, this will start a new range
+ * selection (which will reset the range anchor).
*
- * @param startPos
- * @param endPos
+ * @param pos The anchor position for the selection range.
*/
- public void selectRange(int startPos, int endPos) {
- // In single-select mode, just select the last item in the range.
- if (mSingleSelect) {
- attemptSelect(mAdapter.getModelId(endPos));
- return;
- }
+ void startRangeSelection(int pos) {
+ attemptSelect(mAdapter.getModelId(pos));
+ setSelectionRangeBegin(pos);
+ }
- // In regular (i.e. multi-select) mode
- if (!isRangeSelectionActive()) {
- // If a range selection isn't active, start one up
- attemptSelect(mAdapter.getModelId(startPos));
- setSelectionRangeBegin(startPos);
- }
- // Extend the range selection
- mRanger.snapSelection(endPos);
+ /**
+ * Sets the end point for the current range selection, started by a call to
+ * {@link #startRangeSelection(int)}. This function should only be called when a range selection
+ * is active (see {@link #isRangeSelectionActive()}. Items in the range [anchor, end] will be
+ * selected.
+ *
+ * @param pos The new end position for the selection range.
+ */
+ void snapRangeSelection(int pos) {
+ checkNotNull(mRanger);
+ mRanger.snapSelection(pos);
notifySelectionChanged();
}
/**
+ * Stops an in-progress range selection.
+ */
+ void endRangeSelection() {
+ mRanger = null;
+ }
+
+ /**
* @return Whether or not there is a current range selection active.
*/
- private boolean isRangeSelectionActive() {
+ boolean isRangeSelectionActive() {
return mRanger != null;
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
index d95fb49..9447d9c1 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
@@ -189,6 +189,54 @@
assertSelection(items.get(20));
}
+ public void testRangeSelection() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(19);
+ assertRangeSelection(15, 19);
+ }
+
+ public void testRangeSelection_snapExpand() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(19);
+ mManager.snapRangeSelection(27);
+ assertRangeSelection(15, 27);
+ }
+
+ public void testRangeSelection_snapContract() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(27);
+ mManager.snapRangeSelection(19);
+ assertRangeSelection(15, 19);
+ }
+
+ public void testRangeSelection_snapInvert() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(27);
+ mManager.snapRangeSelection(3);
+ assertRangeSelection(3, 15);
+ }
+
+ public void testRangeSelection_multiple() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(27);
+ mManager.endRangeSelection();
+ mManager.startRangeSelection(42);
+ mManager.snapRangeSelection(57);
+ assertSelectionSize(29);
+ assertRangeSelected(15, 27);
+ assertRangeSelected(42, 57);
+
+ }
+
+ public void testRangeSelection_singleSelect() {
+ mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE, null);
+ mManager.addCallback(mCallback);
+ mManager.startRangeSelection(11);
+ mManager.snapRangeSelection(19);
+ assertSelectionSize(1);
+ assertSelection(items.get(19));
+ }
+
public void testProvisionalSelection() {
Selection s = mManager.getSelection();
assertSelection();
diff --git a/packages/MtpDocumentsProvider/res/values/strings.xml b/packages/MtpDocumentsProvider/res/values/strings.xml
index 43a420c..f3a3fcf 100644
--- a/packages/MtpDocumentsProvider/res/values/strings.xml
+++ b/packages/MtpDocumentsProvider/res/values/strings.xml
@@ -25,4 +25,8 @@
<string name="accessing_notification_title">Accessing files from <xliff:g id="device_model" example="Nexus 9">%1$s</xliff:g></string>
<!-- Description of notification showing Files app is accessing files in a MTP device. [CHAR LIMIT=60]-->
<string name="accessing_notification_description">Don\'t disconnect the device</string>
+ <!-- Error message shown in Files app when the connected MTP device is busy. [CHAR LIMIT=150]-->
+ <string name="error_busy_device">The other device is busy. You can\'t transfer files until it\'s available.</string>
+ <!-- Error message shown in Files app when the connected MTP device may be locked. [CHAR LIMIT=150]-->
+ <string name="error_locked_device">No files found. The other device may be locked. If so, unlock it and try again.</string>
</resources>
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index c456be9..5a11327 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -37,6 +37,7 @@
import android.provider.DocumentsContract.Root;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import java.io.FileNotFoundException;
import java.util.Objects;
@@ -244,15 +245,16 @@
}
/**
- * Returns identifier of single storage if given document points device and it has only one
- * storage. Otherwise null.
+ * Returns document IDs of storages under the given device document.
*
- * @param documentId Document ID that may point a device.
- * @return Identifier for single storage or null.
+ * @param documentId Document ID that points a device.
+ * @return Storage document IDs.
* @throws FileNotFoundException The given document ID is not registered in database.
*/
- @Nullable Identifier getSingleStorageIdentifier(String documentId)
+ String[] getStorageDocumentIds(String documentId)
throws FileNotFoundException {
+ Preconditions.checkArgument(createIdentifier(documentId).mDocumentType ==
+ DOCUMENT_TYPE_DEVICE);
// Check if the parent document is device that has single storage.
try (final Cursor cursor = mDatabase.query(
TABLE_DOCUMENTS,
@@ -267,12 +269,11 @@
null,
null,
null)) {
- if (cursor.getCount() == 1) {
- cursor.moveToNext();
- return createIdentifier(cursor.getString(0));
- } else {
- return null;
+ final String[] ids = new String[cursor.getCount()];
+ for (int i = 0; cursor.moveToNext(); i++) {
+ ids[i] = cursor.getString(0);
}
+ return ids;
}
}
@@ -342,6 +343,26 @@
}
}
+ String getDeviceDocumentId(int deviceId) throws FileNotFoundException {
+ try (final Cursor cursor = mDatabase.query(
+ TABLE_DOCUMENTS,
+ strings(Document.COLUMN_DOCUMENT_ID),
+ COLUMN_DEVICE_ID + " = ? AND " + COLUMN_DOCUMENT_TYPE + " = ? AND " +
+ COLUMN_ROW_STATE + " != ?",
+ strings(deviceId, DOCUMENT_TYPE_DEVICE, ROW_STATE_DISCONNECTED),
+ null,
+ null,
+ null,
+ "1")) {
+ if (cursor.getCount() > 0) {
+ cursor.moveToNext();
+ return cursor.getString(0);
+ } else {
+ throw new FileNotFoundException("The device ID not found: " + deviceId);
+ }
+ }
+ }
+
/**
* Adds new document under the parent.
* The method does not affect invalidated and pending documents because we know the document is
@@ -557,13 +578,9 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion == 1) {
- db.execSQL("DROP TABLE " + TABLE_DOCUMENTS);
- db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA);
- onCreate(db);
- return;
- }
- throw new UnsupportedOperationException();
+ db.execSQL("DROP TABLE " + TABLE_DOCUMENTS);
+ db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA);
+ onCreate(db);
}
}
@@ -680,7 +697,12 @@
if (formatCodeMimeType != null) {
return formatCodeMimeType;
}
- return MediaFile.getMimeTypeForFile(info.getName());
+ final String mediaFileMimeType = MediaFile.getMimeTypeForFile(info.getName());
+ if (mediaFileMimeType != null) {
+ return mediaFileMimeType;
+ }
+ // We don't know the file type.
+ return "application/octet-stream";
}
static String[] strings(Object... args) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
index cb076af..ab356ce 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -30,7 +30,7 @@
* Class containing MtpDatabase constants.
*/
class MtpDatabaseConstants {
- static final int DATABASE_VERSION = 2;
+ static final int DATABASE_VERSION = 3;
static final String DATABASE_NAME = "database";
static final int FLAG_DATABASE_IN_MEMORY = 1;
@@ -125,7 +125,7 @@
COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
COLUMN_ROW_STATE + " INTEGER NOT NULL," +
COLUMN_DOCUMENT_TYPE + " INTEGER NOT NULL," +
- Document.COLUMN_MIME_TYPE + " TEXT," +
+ Document.COLUMN_MIME_TYPE + " TEXT NOT NULL," +
Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
Document.COLUMN_SUMMARY + " TEXT," +
Document.COLUMN_LAST_MODIFIED + " INTEGER," +
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 0338454..a512509 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -20,10 +20,12 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.graphics.Point;
import android.media.MediaFile;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.storage.StorageManager;
@@ -35,6 +37,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.mtp.exceptions.BusyDeviceException;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -163,17 +166,25 @@
try {
openDevice(parentIdentifier.mDeviceId);
if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) {
- final Identifier singleStorageIdentifier =
- mDatabase.getSingleStorageIdentifier(parentDocumentId);
- if (singleStorageIdentifier == null) {
+ final String[] storageDocIds = mDatabase.getStorageDocumentIds(parentDocumentId);
+ if (storageDocIds.length == 0) {
+ // Remote device does not provide storages. Maybe it is locked.
+ return createErrorCursor(projection, R.string.error_locked_device);
+ } else if (storageDocIds.length > 1) {
// Returns storage list from database.
return mDatabase.queryChildDocuments(projection, parentDocumentId);
}
- parentIdentifier = singleStorageIdentifier;
+
+ // Exact one storage is found. Skip storage and returns object in the single
+ // storage.
+ parentIdentifier = mDatabase.createIdentifier(storageDocIds[0]);
}
+
// Returns object list from document loader.
return getDocumentLoader(parentIdentifier).queryChildDocuments(
projection, parentIdentifier);
+ } catch (BusyDeviceException exception) {
+ return createErrorCursor(projection, R.string.error_busy_device);
} catch (IOException exception) {
Log.e(MtpDocumentsProvider.TAG, "queryChildDocuments", exception);
throw new FileNotFoundException(exception.getMessage());
@@ -350,6 +361,16 @@
}
/**
+ * Obtains document ID for the given device ID.
+ * @param deviceId
+ * @return document ID
+ * @throws FileNotFoundException device ID has not been build.
+ */
+ public String getDeviceDocumentId(int deviceId) throws FileNotFoundException {
+ return mDatabase.getDeviceDocumentId(deviceId);
+ }
+
+ /**
* Resumes root scanner to handle the update of device list.
*/
void resumeRootScanner() {
@@ -442,6 +463,21 @@
}
}
+ /**
+ * Creates empty cursor with specific error message.
+ *
+ * @param projection Column names.
+ * @param stringResId String resource ID of error message.
+ * @return Empty cursor with error message.
+ */
+ private Cursor createErrorCursor(String[] projection, int stringResId) {
+ final Bundle bundle = new Bundle();
+ bundle.putString(DocumentsContract.EXTRA_ERROR, mResources.getString(stringResId));
+ final Cursor cursor = new MatrixCursor(projection);
+ cursor.setExtras(bundle);
+ return cursor;
+ }
+
private static class DeviceToolkit {
public final PipeManager mPipeManager;
public final DocumentLoader mDocumentLoader;
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 5519efd..0527790 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -33,6 +33,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.mtp.exceptions.BusyDeviceException;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -101,7 +102,8 @@
}
if (!device.open(connection)) {
- throw new IOException("Failed to open a MTP device.");
+ // We cannot open connection when another application use the device.
+ throw new BusyDeviceException();
}
// Handle devices that fail to obtain storages just after opening a MTP session.
@@ -134,7 +136,7 @@
try {
roots = getRoots(device.getDeviceId());
} catch (IOException exp) {
- Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
+ Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exp);
// If we failed to fetch roots for the device, we still returns device model
// with an empty set of roots so that the device is shown DocumentsUI as long as
// the device is physically connected.
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
index c7206a7..84745b2 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
@@ -17,10 +17,15 @@
package com.android.mtp;
import android.app.Activity;
-import android.content.ComponentName;
import android.content.Intent;
+import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
+import android.net.Uri;
import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.util.Log;
+
+import java.io.IOException;
/**
* Invisible activity to receive intents.
@@ -33,14 +38,21 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(getIntent().getAction())) {
- // TODO: To obtain data URI for the attached device, we need to wait until RootScanner
- // found the device and add it to database. Set correct root URI, and use ACTION_BROWSE
- // to launch Documents UI.
- final Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.setComponent(new ComponentName(
- "com.android.documentsui", "com.android.documentsui.LauncherActivity"));
- this.startActivity(intent);
+ final UsbDevice device = getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ try {
+ final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
+ provider.openDevice(device.getDeviceId());
+ final String deviceRootId = provider.getDeviceDocumentId(device.getDeviceId());
+ final Uri uri = DocumentsContract.buildRootUri(
+ MtpDocumentsProvider.AUTHORITY, deviceRootId);
+
+ final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE);
+ intent.setData(uri);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ this.startActivity(intent);
+ } catch (IOException exception) {
+ Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exception);
+ }
}
finish();
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java b/packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java
new file mode 100644
index 0000000..55f55b0
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java
@@ -0,0 +1,25 @@
+/*
+ * 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.mtp.exceptions;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when the device is busy and the requested operation cannon be completed.
+ */
+public class BusyDeviceException extends IOException {
+}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 5b0f557..01fcc55 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -29,6 +29,8 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
+import com.android.mtp.exceptions.BusyDeviceException;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
@@ -526,6 +528,52 @@
}
}
+ public void testBusyDevice() throws Exception {
+ mMtpManager = new TestMtpManager(getContext()) {
+ @Override
+ void openDevice(int deviceId) throws IOException {
+ throw new BusyDeviceException();
+ }
+ };
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 0, "Device A", false /* unopened */, new MtpRoot[0], null, null));
+
+ mProvider.resumeRootScanner();
+ mResolver.waitForNotification(ROOTS_URI, 1);
+
+ try (final Cursor cursor = mProvider.queryRoots(null)) {
+ assertEquals(1, cursor.getCount());
+ }
+
+ try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {
+ assertEquals(0, cursor.getCount());
+ assertEquals(
+ "error_busy_device",
+ cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
+ }
+ }
+
+ public void testLockedDevice() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 0, "Device A", false /* unopened */, new MtpRoot[0], null, null));
+
+ mProvider.resumeRootScanner();
+ mResolver.waitForNotification(ROOTS_URI, 1);
+
+ try (final Cursor cursor = mProvider.queryRoots(null)) {
+ assertEquals(1, cursor.getCount());
+ }
+
+ try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {
+ assertEquals(0, cursor.getCount());
+ assertEquals(
+ "error_locked_device",
+ cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
+ }
+ }
+
private void setupProvider(int flag) {
mDatabase = new MtpDatabase(getContext(), flag);
mProvider = new MtpDocumentsProvider();
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
index b23038b..8676b5a 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
@@ -24,6 +24,10 @@
switch (id) {
case R.string.root_name:
return "%1$s %2$s";
+ case R.string.error_busy_device:
+ return "error_busy_device";
+ case R.string.error_locked_device:
+ return "error_locked_device";
}
throw new NotFoundException();
}
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 8c555a6..bad7e20 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -88,8 +88,8 @@
* <p>
* The workflow is:
* <ol>
- * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with its pid and the
- * estimated total effort.
+ * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with a sequential id,
+ * its pid, and the estimated total effort.
* <li>{@link BugreportReceiver} receives the intent and delegates it to this service.
* <li>Upon start, this service:
* <ol>
@@ -132,6 +132,7 @@
static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
+ static final String EXTRA_ID = "android.intent.extra.ID";
static final String EXTRA_PID = "android.intent.extra.PID";
static final String EXTRA_MAX = "android.intent.extra.MAX";
static final String EXTRA_NAME = "android.intent.extra.NAME";
@@ -177,7 +178,7 @@
*/
private static final String SCREENSHOT_DIR = "bugreports";
- /** Managed dumpstate processes (keyed by pid) */
+ /** Managed dumpstate processes (keyed by id) */
private final SparseArray<BugreportInfo> mProcesses = new SparseArray<>();
private Context mContext;
@@ -222,7 +223,7 @@
}
// If service is killed it cannot be recreated because it would not know which
- // dumpstate PIDs it would have to watch.
+ // dumpstate IDs it would have to watch.
return START_NOT_STICKY;
}
@@ -299,38 +300,41 @@
}
final String action = intent.getAction();
final int pid = intent.getIntExtra(EXTRA_PID, 0);
+ // TODO: temporarily using pid as id until test cases and dumpstate are changed.
+ final int id = intent.getIntExtra(EXTRA_ID, pid);
final int max = intent.getIntExtra(EXTRA_MAX, -1);
final String name = intent.getStringExtra(EXTRA_NAME);
- if (DEBUG) Log.v(TAG, "action: " + action + ", name: " + name + ", pid: " + pid
- + ", max: "+ max);
+ if (DEBUG)
+ Log.v(TAG, "action: " + action + ", name: " + name + ", id: " + id + ", pid: "
+ + pid + ", max: " + max);
switch (action) {
case INTENT_BUGREPORT_STARTED:
- if (!startProgress(name, pid, max)) {
+ if (!startProgress(name, id, pid, max)) {
stopSelfWhenDone();
return;
}
poll();
break;
case INTENT_BUGREPORT_FINISHED:
- if (pid == 0) {
+ if (id == 0) {
// Shouldn't happen, unless BUGREPORT_FINISHED is received from a legacy,
// out-of-sync dumpstate process.
- Log.w(TAG, "Missing " + EXTRA_PID + " on intent " + intent);
+ Log.w(TAG, "Missing " + EXTRA_ID + " on intent " + intent);
}
- onBugreportFinished(pid, intent);
+ onBugreportFinished(id, intent);
break;
case INTENT_BUGREPORT_INFO_LAUNCH:
- launchBugreportInfoDialog(pid);
+ launchBugreportInfoDialog(id);
break;
case INTENT_BUGREPORT_SCREENSHOT:
- takeScreenshot(pid, true);
+ takeScreenshot(id, true);
break;
case INTENT_BUGREPORT_SHARE:
- shareBugreport(pid, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
+ shareBugreport(id, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
break;
case INTENT_BUGREPORT_CANCEL:
- cancel(pid);
+ cancel(id);
break;
default:
Log.w(TAG, "Unsupported intent: " + action);
@@ -367,10 +371,10 @@
}
}
- private BugreportInfo getInfo(int pid) {
- final BugreportInfo info = mProcesses.get(pid);
+ private BugreportInfo getInfo(int id) {
+ final BugreportInfo info = mProcesses.get(id);
if (info == null) {
- Log.w(TAG, "Not monitoring process with PID " + pid);
+ Log.w(TAG, "Not monitoring process with ID " + id);
}
return info;
}
@@ -381,10 +385,14 @@
*
* @return whether it succeeded or not.
*/
- private boolean startProgress(String name, int pid, int max) {
+ private boolean startProgress(String name, int id, int pid, int max) {
if (name == null) {
Log.w(TAG, "Missing " + EXTRA_NAME + " on start intent");
}
+ if (id == -1) {
+ Log.e(TAG, "Missing " + EXTRA_ID + " on start intent");
+ return false;
+ }
if (pid == -1) {
Log.e(TAG, "Missing " + EXTRA_PID + " on start intent");
return false;
@@ -394,14 +402,14 @@
return false;
}
- final BugreportInfo info = new BugreportInfo(mContext, pid, name, max);
- if (mProcesses.indexOfKey(pid) >= 0) {
- Log.w(TAG, "PID " + pid + " already watched");
+ final BugreportInfo info = new BugreportInfo(mContext, id, pid, name, max);
+ if (mProcesses.indexOfKey(id) >= 0) {
+ Log.w(TAG, "ID " + id + " already watched");
} else {
- mProcesses.put(info.pid, info);
+ mProcesses.put(info.id, info);
}
// Take initial screenshot.
- takeScreenshot(pid, false);
+ takeScreenshot(id, false);
updateProgress(info);
return true;
}
@@ -423,22 +431,22 @@
com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build();
final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH);
- infoIntent.putExtra(EXTRA_PID, info.pid);
+ infoIntent.putExtra(EXTRA_ID, info.id);
final Action infoAction = new Action.Builder(null,
mContext.getString(R.string.bugreport_info_action),
- PendingIntent.getService(mContext, info.pid, infoIntent,
+ PendingIntent.getService(mContext, info.id, infoIntent,
PendingIntent.FLAG_UPDATE_CURRENT)).build();
final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class);
screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT);
- screenshotIntent.putExtra(EXTRA_PID, info.pid);
+ screenshotIntent.putExtra(EXTRA_ID, info.id);
PendingIntent screenshotPendingIntent = mTakingScreenshot ? null : PendingIntent
- .getService(mContext, info.pid, screenshotIntent,
+ .getService(mContext, info.id, screenshotIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
final Action screenshotAction = new Action.Builder(null,
mContext.getString(R.string.bugreport_screenshot_action),
screenshotPendingIntent).build();
- final String title = mContext.getString(R.string.bugreport_in_progress_title, info.pid);
+ final String title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
final String name =
info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed);
@@ -464,8 +472,8 @@
+ info + ")");
return;
}
- Log.v(TAG, "Sending 'Progress' notification for pid " + info.pid + ": " + percentText);
- NotificationManager.from(mContext).notify(TAG, info.pid, notification);
+ Log.v(TAG, "Sending 'Progress' notification for id " + info.id + ": " + percentText);
+ NotificationManager.from(mContext).notify(TAG, info.id, notification);
}
/**
@@ -474,38 +482,38 @@
private static PendingIntent newCancelIntent(Context context, BugreportInfo info) {
final Intent intent = new Intent(INTENT_BUGREPORT_CANCEL);
intent.setClass(context, BugreportProgressService.class);
- intent.putExtra(EXTRA_PID, info.pid);
- return PendingIntent.getService(context, info.pid, intent,
+ intent.putExtra(EXTRA_ID, info.id);
+ return PendingIntent.getService(context, info.id, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
/**
* Finalizes the progress on a given bugreport and cancel its notification.
*/
- private void stopProgress(int pid) {
- if (mProcesses.indexOfKey(pid) < 0) {
- Log.w(TAG, "PID not watched: " + pid);
+ private void stopProgress(int id) {
+ if (mProcesses.indexOfKey(id) < 0) {
+ Log.w(TAG, "ID not watched: " + id);
} else {
- Log.d(TAG, "Removing PID " + pid);
- mProcesses.remove(pid);
+ Log.d(TAG, "Removing ID " + id);
+ mProcesses.remove(id);
}
stopSelfWhenDone();
- Log.v(TAG, "stopProgress(" + pid + "): cancel notification");
- NotificationManager.from(mContext).cancel(TAG, pid);
+ Log.v(TAG, "stopProgress(" + id + "): cancel notification");
+ NotificationManager.from(mContext).cancel(TAG, id);
}
/**
* Cancels a bugreport upon user's request.
*/
- private void cancel(int pid) {
- Log.v(TAG, "cancel: pid=" + pid);
- final BugreportInfo info = getInfo(pid);
+ private void cancel(int id) {
+ Log.v(TAG, "cancel: ID=" + id);
+ final BugreportInfo info = getInfo(id);
if (info != null && !info.finished) {
- Log.i(TAG, "Cancelling bugreport service (pid=" + pid + ") on user's request");
+ Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request");
setSystemProperty(CTL_STOP, BUGREPORT_SERVICE);
deleteScreenshots(info);
}
- stopProgress(pid);
+ stopProgress(id);
}
/**
@@ -522,14 +530,15 @@
for (int i = 0; i < total; i++) {
final BugreportInfo info = mProcesses.valueAt(i);
if (info == null) {
- Log.wtf(TAG, "pollProgress(): null info at index " + i + "(pid = "
+ Log.wtf(TAG, "pollProgress(): null info at index " + i + "(ID = "
+ mProcesses.keyAt(i) + ")");
continue;
}
final int pid = info.pid;
+ final int id = info.id;
if (info.finished) {
- if (DEBUG) Log.v(TAG, "Skipping finished process " + pid);
+ if (DEBUG) Log.v(TAG, "Skipping finished process " + pid + "(id: " + id + ")");
continue;
}
activeProcesses++;
@@ -544,13 +553,13 @@
if (progressChanged || maxChanged) {
if (progressChanged) {
- if (DEBUG) Log.v(TAG, "Updating progress for PID " + pid + " from "
- + info.progress + " to " + progress);
+ if (DEBUG) Log.v(TAG, "Updating progress for PID " + pid + "(id: " + id
+ + ") from " + info.progress + " to " + progress);
info.progress = progress;
}
if (maxChanged) {
- Log.i(TAG, "Updating max progress for PID " + pid + " from " + info.max
- + " to " + max);
+ Log.i(TAG, "Updating max progress for PID " + pid + "(id: " + id
+ + ") from " + info.max + " to " + max);
info.max = max;
}
info.lastUpdate = System.currentTimeMillis();
@@ -558,9 +567,9 @@
} else {
long inactiveTime = System.currentTimeMillis() - info.lastUpdate;
if (inactiveTime >= INACTIVITY_TIMEOUT) {
- Log.w(TAG, "No progress update for process " + pid + " since "
+ Log.w(TAG, "No progress update for PID " + pid + " since "
+ info.getFormattedLastUpdate());
- stopProgress(info.pid);
+ stopProgress(info.id);
}
}
}
@@ -572,19 +581,16 @@
* Fetches a {@link BugreportInfo} for a given process and launches a dialog where the user can
* change its values.
*/
- private void launchBugreportInfoDialog(int pid) {
+ private void launchBugreportInfoDialog(int id) {
// Copy values so it doesn't lock mProcesses while UI is being updated
final String name, title, description;
- final BugreportInfo info = getInfo(pid);
+ final BugreportInfo info = getInfo(id);
if (info == null) {
return;
}
- name = info.name;
- title = info.title;
- description = info.description;
collapseNotificationBar();
- mInfoDialog.initialize(mContext, pid, name, title, description);
+ mInfoDialog.initialize(mContext, info);
}
/**
@@ -597,7 +603,7 @@
* Typical usage is delaying when taken from the notification action, and taking it right away
* upon receiving a {@link #INTENT_BUGREPORT_STARTED}.
*/
- private void takeScreenshot(int pid, boolean delayed) {
+ private void takeScreenshot(int id, boolean delayed) {
setTakingScreenshot(true);
if (delayed) {
collapseNotificationBar();
@@ -608,28 +614,28 @@
// Show a toast just once, otherwise it might be captured in the screenshot.
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
- takeScreenshot(pid, SCREENSHOT_DELAY_SECONDS);
+ takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
} else {
- takeScreenshot(pid, 0);
+ takeScreenshot(id, 0);
}
}
/**
* Takes a screenshot after {@code delay} seconds.
*/
- private void takeScreenshot(int pid, int delay) {
+ private void takeScreenshot(int id, int delay) {
if (delay > 0) {
- Log.d(TAG, "Taking screenshot for " + pid + " in " + delay + " seconds");
+ Log.d(TAG, "Taking screenshot for " + id + " in " + delay + " seconds");
final Message msg = mMainHandler.obtainMessage();
msg.what = MSG_DELAYED_SCREENSHOT;
- msg.arg1 = pid;
+ msg.arg1 = id;
msg.arg2 = delay - 1;
mMainHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS);
return;
}
// It's time to take the screenshot: let the proper thread handle it
- final BugreportInfo info = getInfo(pid);
+ final BugreportInfo info = getInfo(id);
if (info == null) {
return;
}
@@ -638,7 +644,7 @@
final Message requestMsg = new Message();
requestMsg.what = MSG_SCREENSHOT_REQUEST;
- requestMsg.arg1 = pid;
+ requestMsg.arg1 = id;
requestMsg.obj = screenshotPath;
mScreenshotHandler.sendMessage(requestMsg);
}
@@ -715,30 +721,30 @@
*/
private void stopSelfWhenDone() {
if (mProcesses.size() > 0) {
- if (DEBUG) Log.v(TAG, "Staying alive, waiting for pids " + mProcesses);
+ if (DEBUG) Log.d(TAG, "Staying alive, waiting for IDs " + mProcesses);
return;
}
- Log.v(TAG, "No more pids to handle, shutting down");
+ Log.v(TAG, "No more processes to handle, shutting down");
stopSelf();
}
/**
* Handles the BUGREPORT_FINISHED intent sent by {@code dumpstate}.
*/
- private void onBugreportFinished(int pid, Intent intent) {
+ private void onBugreportFinished(int id, Intent intent) {
final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
if (bugreportFile == null) {
// Should never happen, dumpstate always set the file.
Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
return;
}
- mInfoDialog.onBugreportFinished(pid);
- BugreportInfo info = getInfo(pid);
+ mInfoDialog.onBugreportFinished(id);
+ BugreportInfo info = getInfo(id);
if (info == null) {
// Happens when BUGREPORT_FINISHED was received without a BUGREPORT_STARTED first.
- Log.v(TAG, "Creating info for untracked pid " + pid);
- info = new BugreportInfo(mContext, pid);
- mProcesses.put(pid, info);
+ Log.v(TAG, "Creating info for untracked ID " + id);
+ info = new BugreportInfo(mContext, id);
+ mProcesses.put(id, info);
}
info.renameScreenshots(mScreenshotsDir);
info.bugreportFile = bugreportFile;
@@ -765,7 +771,7 @@
if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
Toast.makeText(context, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show();
- stopProgress(info.pid);
+ stopProgress(info.id);
return;
}
@@ -837,12 +843,12 @@
* Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE}
* intent, but issuing a warning dialog the first time.
*/
- private void shareBugreport(int pid, BugreportInfo sharedInfo) {
- BugreportInfo info = getInfo(pid);
+ private void shareBugreport(int id, BugreportInfo sharedInfo) {
+ BugreportInfo info = getInfo(id);
if (info == null) {
// Service was terminated but notification persisted
info = sharedInfo;
- Log.d(TAG, "shareBugreport(): no info for PID " + pid + " on managed processes ("
+ Log.d(TAG, "shareBugreport(): no info for ID " + id + " on managed processes ("
+ mProcesses + "), using info from intent instead (" + info + ")");
}
@@ -863,7 +869,7 @@
mContext.startActivity(notifIntent);
// ... and stop watching this process.
- stopProgress(pid);
+ stopProgress(id);
}
/**
@@ -877,16 +883,16 @@
final Intent shareIntent = new Intent(INTENT_BUGREPORT_SHARE);
shareIntent.setClass(context, BugreportProgressService.class);
shareIntent.setAction(INTENT_BUGREPORT_SHARE);
- shareIntent.putExtra(EXTRA_PID, info.pid);
+ shareIntent.putExtra(EXTRA_ID, info.id);
shareIntent.putExtra(EXTRA_INFO, info);
- final String title = context.getString(R.string.bugreport_finished_title, info.pid);
+ final String title = context.getString(R.string.bugreport_finished_title, info.id);
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setContentTitle(title)
.setTicker(title)
.setContentText(context.getString(R.string.bugreport_finished_text))
- .setContentIntent(PendingIntent.getService(context, info.pid, shareIntent,
+ .setContentIntent(PendingIntent.getService(context, info.id, shareIntent,
PendingIntent.FLAG_UPDATE_CURRENT))
.setDeleteIntent(newCancelIntent(context, info))
.setLocalOnly(true)
@@ -897,8 +903,8 @@
builder.setContentInfo(info.name);
}
- Log.v(TAG, "Sending 'Share' notification for pid " + info.pid + ": " + title);
- NotificationManager.from(context).notify(TAG, info.pid, builder.build());
+ Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
+ NotificationManager.from(context).notify(TAG, info.id, builder.build());
}
/**
@@ -906,7 +912,7 @@
* finishes - at this point there is nothing to be done other than waiting, hence it has no
* pending action.
*/
- private static void sendBugreportBeingUpdatedNotification(Context context, int pid) {
+ private static void sendBugreportBeingUpdatedNotification(Context context, int id) {
final String title = context.getString(R.string.bugreport_updating_title);
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
@@ -916,8 +922,8 @@
.setLocalOnly(true)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color));
- Log.v(TAG, "Sending 'Updating zip' notification for pid " + pid + ": " + title);
- NotificationManager.from(context).notify(TAG, pid, builder.build());
+ Log.v(TAG, "Sending 'Updating zip' notification for ID " + id + ": " + title);
+ NotificationManager.from(context).notify(TAG, id, builder.build());
}
/**
@@ -985,7 +991,7 @@
// It's not possible to add a new entry into an existing file, so we need to create a new
// zip, copy all entries, then rename it.
- sendBugreportBeingUpdatedNotification(context, info.pid); // ...and that takes time
+ sendBugreportBeingUpdatedNotification(context, info.id); // ...and that takes time
final File dir = info.bugreportFile.getParentFile();
final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName());
Log.d(TAG, "Writing temporary zip file (" + tmpZip + ") with title and/or description");
@@ -1113,8 +1119,8 @@
/**
* Updates the user-provided details of a bugreport.
*/
- private void updateBugreportInfo(int pid, String name, String title, String description) {
- final BugreportInfo info = getInfo(pid);
+ private void updateBugreportInfo(int id, String name, String title, String description) {
+ final BugreportInfo info = getInfo(id);
if (info == null) {
return;
}
@@ -1179,6 +1185,7 @@
private EditText mInfoDescription;
private AlertDialog mDialog;
private Button mOkButton;
+ private int mId;
private int mPid;
/**
@@ -1207,8 +1214,7 @@
/**
* Sets its internal state and displays the dialog.
*/
- private void initialize(Context context, int pid, String name, String title,
- String description) {
+ private void initialize(Context context, BugreportInfo info) {
// First initializes singleton.
if (mDialog == null) {
@SuppressLint("InflateParams")
@@ -1232,7 +1238,7 @@
mDialog = new AlertDialog.Builder(context)
.setView(view)
- .setTitle(context.getString(R.string.bugreport_info_dialog_title, pid))
+ .setTitle(context.getString(R.string.bugreport_info_dialog_title, info.id))
.setCancelable(false)
.setPositiveButton(context.getString(com.android.internal.R.string.ok),
null)
@@ -1258,16 +1264,17 @@
}
// Then set fields.
- mSavedName = mTempName = name;
- mPid = pid;
- if (!TextUtils.isEmpty(name)) {
- mInfoName.setText(name);
+ mSavedName = mTempName = info.name;
+ mId = info.id;
+ mPid = info.pid;
+ if (!TextUtils.isEmpty(info.name)) {
+ mInfoName.setText(info.name);
}
- if (!TextUtils.isEmpty(title)) {
- mInfoTitle.setText(title);
+ if (!TextUtils.isEmpty(info.title)) {
+ mInfoTitle.setText(info.title);
}
- if (!TextUtils.isEmpty(description)) {
- mInfoDescription.setText(description);
+ if (!TextUtils.isEmpty(info.description)) {
+ mInfoDescription.setText(info.description);
}
// And finally display it.
@@ -1290,7 +1297,7 @@
final String title = mInfoTitle.getText().toString();
final String description = mInfoDescription.getText().toString();
- updateBugreportInfo(mPid, name, title, description);
+ updateBugreportInfo(mId, name, title, description);
mDialog.dismiss();
}
});
@@ -1328,7 +1335,7 @@
// Must update system property for the cases where dumpstate finishes
// while the user is still entering other fields (like title or
// description)
- setBugreportNameProperty(mPid, name);
+ setBugreportNameProperty(mId, name);
}
/**
@@ -1337,7 +1344,7 @@
* <p>Once the bugreport is finished dumpstate has already generated the final files, so
* changing the name would have no effect.
*/
- private void onBugreportFinished(int pid) {
+ private void onBugreportFinished(int id) {
if (mInfoName != null) {
mInfoName.setEnabled(false);
mInfoName.setText(mSavedName);
@@ -1353,6 +1360,11 @@
private final Context context;
/**
+ * Sequential, user-friendly id used to identify the bugreport.
+ */
+ final int id;
+
+ /**
* {@code pid} of the {@code dumpstate} process generating the bugreport.
*/
final int pid;
@@ -1426,8 +1438,9 @@
/**
* Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
*/
- BugreportInfo(Context context, int pid, String name, int max) {
+ BugreportInfo(Context context, int id, int pid, String name, int max) {
this.context = context;
+ this.id = id;
this.pid = pid;
this.name = name;
this.max = max;
@@ -1437,8 +1450,8 @@
* Constructor for untracked bugreports - typically called upon receiving BUGREPORT_FINISHED
* without a previous call to BUGREPORT_STARTED.
*/
- BugreportInfo(Context context, int pid) {
- this(context, pid, null, 0);
+ BugreportInfo(Context context, int id) {
+ this(context, id, id, null, 0);
this.finished = true;
}
@@ -1494,7 +1507,7 @@
@Override
public String toString() {
final float percent = ((float) progress * 100 / max);
- return "pid: " + pid + ", name: " + name + ", finished: " + finished
+ return "id: " + id + ", pid: " + pid + ", name: " + name + ", finished: " + finished
+ "\n\ttitle: " + title + "\n\tdescription: " + description
+ "\n\tfile: " + bugreportFile + "\n\tscreenshots: " + screenshotFiles
+ "\n\tprogress: " + progress + "/" + max + "(" + percent + ")"
@@ -1506,6 +1519,7 @@
// Parcelable contract
protected BugreportInfo(Parcel in) {
context = null;
+ id = in.readInt();
pid = in.readInt();
name = in.readString();
title = in.readString();
@@ -1527,6 +1541,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(id);
dest.writeInt(pid);
dest.writeString(name);
dest.writeString(title);
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a9b8df2..4cd920a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -52,8 +52,12 @@
<!-- Tint color for the content on the notification overflow card. -->
<color name="keyguard_overflow_content_color">#ff686868</color>
+ <!-- The disabled recents task bar background color. -->
+ <color name="recents_task_bar_disabled_background_color">#ff676767</color>
<!-- The default recents task bar background color. -->
<color name="recents_task_bar_default_background_color">#ffe6e6e6</color>
+ <!-- The default recents task view background color. -->
+ <color name="recents_task_view_default_background_color">#fff3f3f3</color>
<!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
<color name="recents_task_bar_light_text_color">#ffeeeeee</color>
<!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5d4789a..4116962 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -714,6 +714,8 @@
<string name="recents_search_bar_label">search</string>
<!-- Recents: Launch error string. [CHAR LIMIT=NONE] -->
<string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
+ <!-- Recents: Launch disabled string. [CHAR LIMIT=NONE] -->
+ <string name="recents_launch_disabled_message"><xliff:g id="app" example="Calendar">%s</xliff:g> is disabled in safe-mode.</string>
<!-- Recents: Show history string. [CHAR LIMIT=NONE] -->
<string name="recents_history_button_label">History</string>
<!-- Recents: History clear all string. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 454d1ce..4845425 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -62,6 +62,7 @@
mPlusPaint;
private float mTextHeight, mWarningTextHeight;
private int mIconTint = Color.WHITE;
+ private float mOldDarkIntensity = 0f;
private int mHeight;
private int mWidth;
@@ -295,6 +296,9 @@
}
public void setDarkIntensity(float darkIntensity) {
+ if (darkIntensity == mOldDarkIntensity) {
+ return;
+ }
int backgroundColor = getBackgroundColor(darkIntensity);
int fillColor = getFillColor(darkIntensity);
mIconTint = fillColor;
@@ -302,6 +306,7 @@
mBoltPaint.setColor(fillColor);
mChargeColor = fillColor;
invalidateSelf();
+ mOldDarkIntensity = darkIntensity;
}
private int getBackgroundColor(float darkIntensity) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 9da5c2b..e0efaa5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -837,11 +837,13 @@
* Draws the header of a task used for the window animation into a bitmap.
*/
private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
if (toTransform != null && toTask.key != null) {
Bitmap thumbnail;
synchronized (mHeaderBarLock) {
int toHeaderWidth = (int) toTransform.rect.width();
int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+ boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(),
(int) toTransform.rect.height());
thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
@@ -851,7 +853,8 @@
} else {
Canvas c = new Canvas(thumbnail);
c.scale(toTransform.scale, toTransform.scale);
- mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */);
+ mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */,
+ disabledInSafeMode);
mHeaderBar.draw(c);
c.setBitmap(null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index 244c0df..95aa10f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents.misc;
import android.os.Handler;
+import android.view.ViewDebug;
/**
* A dozer is a class that fires a trigger after it falls asleep.
@@ -26,8 +27,11 @@
Handler mHandler;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mIsDozing;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mHasTriggered;
+ @ViewDebug.ExportedProperty(category="recents")
int mDozeDurationMilliseconds;
Runnable mOnSleepRunnable;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 22ab794..8b4474f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -112,6 +112,8 @@
Display mDisplay;
String mRecentsPackage;
ComponentName mAssistComponent;
+
+ boolean mIsSafeMode;
boolean mHasFreeformWorkspaceSupport;
Bitmap mDummyIcon;
@@ -137,6 +139,7 @@
mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) ||
Settings.Global.getInt(context.getContentResolver(),
DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+ mIsSafeMode = mPm.isSafeMode();
// Get the dummy thumbnail width/heights
Resources res = context.getResources();
@@ -187,7 +190,8 @@
rti.firstActiveTime = rti.lastActiveTime = i;
if (i % 2 == 0) {
rti.taskDescription = new ActivityManager.TaskDescription(description,
- Bitmap.createBitmap(mDummyIcon),
+ Bitmap.createBitmap(mDummyIcon), null,
+ 0xFF000000 | (0xFFFFFF & new Random().nextInt()),
0xFF000000 | (0xFFFFFF & new Random().nextInt()));
} else {
rti.taskDescription = new ActivityManager.TaskDescription();
@@ -260,6 +264,13 @@
return mHasFreeformWorkspaceSupport;
}
+ /**
+ * Returns whether this device is in the safe mode.
+ */
+ public boolean isInSafeMode() {
+ return mIsSafeMode;
+ }
+
/** Returns whether the recents is currently running */
public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
MutableBoolean isHomeTopMost) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index c51aa7c..016e6d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -188,11 +189,15 @@
: null;
Bitmap thumbnail = loader.getAndUpdateThumbnail(taskKey, false);
int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
+ int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
+ boolean isSystemApp = (loader.getAndUpdateActivityInfo(taskKey).applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0;
// Add the task to the stack
Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
thumbnail, title, contentDescription, dismissDescription, activityColor,
- !isStackTask, isLaunchTarget, t.bounds, t.taskDescription);
+ backgroundColor, !isStackTask, isLaunchTarget, isSystemApp, t.bounds,
+ t.taskDescription);
allTasks.add(task);
affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 26130ab..5e1af12 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -264,13 +264,16 @@
private int mNumVisibleThumbnailsLoaded;
int mDefaultTaskBarBackgroundColor;
+ int mDefaultTaskViewBackgroundColor;
BitmapDrawable mDefaultIcon;
Bitmap mDefaultThumbnail;
public RecentsTaskLoader(Context context) {
Resources res = context.getResources();
mDefaultTaskBarBackgroundColor =
- res.getColor(R.color.recents_task_bar_default_background_color);
+ context.getColor(R.color.recents_task_bar_default_background_color);
+ mDefaultTaskViewBackgroundColor =
+ context.getColor(R.color.recents_task_view_default_background_color);
mMaxThumbnailCacheSize = res.getInteger(R.integer.config_recents_max_thumbnail_count);
mMaxIconCacheSize = res.getInteger(R.integer.config_recents_max_icon_count);
int iconCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 :
@@ -556,10 +559,20 @@
}
/**
+ * Returns the task's background color if possible.
+ */
+ int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
+ if (td != null && td.getBackgroundColor() != 0) {
+ return td.getBackgroundColor();
+ }
+ return mDefaultTaskViewBackgroundColor;
+ }
+
+ /**
* Returns the activity info for the given task key, retrieving one from the system if the
* task key is expired.
*/
- private ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
+ ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
SystemServicesProxy ssp = Recents.getSystemServices();
ComponentName cn = taskKey.getComponent();
ActivityInfo activityInfo = mActivityInfoCache.get(cn);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 1c277d5..8ed6dd7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -23,6 +23,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.view.ViewDebug;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -48,11 +49,17 @@
/* The Task Key represents the unique primary key for the task */
public static class TaskKey {
+ @ViewDebug.ExportedProperty(category="recents")
public final int id;
+ @ViewDebug.ExportedProperty(category="recents")
public int stackId;
+ @ViewDebug.ExportedProperty(category="recents")
public final Intent baseIntent;
+ @ViewDebug.ExportedProperty(category="recents")
public final int userId;
+ @ViewDebug.ExportedProperty(category="recents")
public long firstActiveTime;
+ @ViewDebug.ExportedProperty(category="recents")
public long lastActiveTime;
private int mHashCode;
@@ -105,17 +112,21 @@
}
}
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="key_")
public TaskKey key;
/**
* The group will be computed separately from the initialization of the task
*/
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="group_")
public TaskGrouping group;
/**
* The affiliationTaskId is the task id of the parent task or itself if it is not affiliated
* with any task.
*/
+ @ViewDebug.ExportedProperty(category="recents")
public int affiliationTaskId;
+ @ViewDebug.ExportedProperty(category="recents")
public int affiliationColor;
/**
@@ -124,15 +135,23 @@
*/
public Drawable icon;
public Bitmap thumbnail;
+ @ViewDebug.ExportedProperty(category="recents")
public String title;
+ @ViewDebug.ExportedProperty(category="recents")
public String contentDescription;
+ @ViewDebug.ExportedProperty(category="recents")
public String dismissDescription;
+ @ViewDebug.ExportedProperty(category="recents")
public int colorPrimary;
+ @ViewDebug.ExportedProperty(category="recents")
+ public int colorBackground;
+ @ViewDebug.ExportedProperty(category="recents")
public boolean useLightOnPrimaryColor;
/**
* The bounds of the task, used only if it is a freeform task.
*/
+ @ViewDebug.ExportedProperty(category="recents")
public Rect bounds;
/**
@@ -143,8 +162,12 @@
/**
* The state isLaunchTarget will be set for the correct task upon launching Recents.
*/
+ @ViewDebug.ExportedProperty(category="recents")
public boolean isLaunchTarget;
+ @ViewDebug.ExportedProperty(category="recents")
public boolean isHistorical;
+ @ViewDebug.ExportedProperty(category="recents")
+ public boolean isSystemApp;
private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
@@ -154,8 +177,8 @@
public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
Bitmap thumbnail, String title, String contentDescription,
- String dismissDescription, int colorPrimary, boolean isHistorical,
- boolean isLaunchTarget, Rect bounds,
+ String dismissDescription, int colorPrimary, int colorBackground,
+ boolean isHistorical, boolean isLaunchTarget, boolean isSystemApp, Rect bounds,
ActivityManager.TaskDescription taskDescription) {
boolean isInAffiliationGroup = (affiliationTaskId != key.id);
boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
@@ -168,12 +191,14 @@
this.contentDescription = contentDescription;
this.dismissDescription = dismissDescription;
this.colorPrimary = hasAffiliationGroupColor ? affiliationColor : colorPrimary;
+ this.colorBackground = colorBackground;
this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
Color.WHITE) > 3f;
this.bounds = bounds;
this.taskDescription = taskDescription;
this.isLaunchTarget = isLaunchTarget;
this.isHistorical = isHistorical;
+ this.isSystemApp = isSystemApp;
}
/** Copies the other task. */
@@ -188,10 +213,12 @@
this.contentDescription = o.contentDescription;
this.dismissDescription = o.dismissDescription;
this.colorPrimary = o.colorPrimary;
+ this.colorBackground = o.colorBackground;
this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
this.bounds = o.bounds;
this.isLaunchTarget = o.isLaunchTarget;
this.isHistorical = o.isHistorical;
+ this.isSystemApp = o.isSystemApp;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index 5842095..4a65374 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -19,16 +19,22 @@
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
/* An outline provider that has a clip and outline that can be animated. */
public class AnimateableViewBounds extends ViewOutlineProvider {
View mSourceView;
+ @ViewDebug.ExportedProperty(category="recents")
Rect mClipRect = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
Rect mClipBounds = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
Rect mLastClipBounds = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
int mCornerRadius;
+ @ViewDebug.ExportedProperty(category="recents")
float mAlpha = 1f;
final float mMinAlpha = 0.25f;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 42aaa97..c4db485 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents.views;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -32,6 +34,7 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
@@ -79,8 +82,6 @@
import java.util.ArrayList;
import java.util.List;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
/**
* This view is the the top level layout that contains TaskStacks (which are laid out according
* to their SpaceNode bounds.
@@ -103,6 +104,8 @@
private boolean mAwaitingFirstLayout = true;
private boolean mLastTaskLaunchedWasFreeform;
+
+ @ViewDebug.ExportedProperty(category="recents")
private Rect mSystemInsets = new Rect();
private int mDividerSize;
@@ -110,6 +113,7 @@
private Animator mBackgroundScrimAnimator;
private RecentsTransitionHelper mTransitionHelper;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
private RecentsViewTouchHandler mTouchHandler;
private final FlingAnimationUtils mFlingAnimationUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 346ce16..016d937 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -20,6 +20,7 @@
import android.graphics.Point;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import android.view.ViewDebug;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
@@ -61,12 +62,18 @@
private RecentsView mRv;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task")
private Task mDragTask;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task_view_")
private TaskView mTaskView;
+ @ViewDebug.ExportedProperty(category="recents")
private Point mTaskViewOffset = new Point();
+ @ViewDebug.ExportedProperty(category="recents")
private Point mDownPos = new Point();
+ @ViewDebug.ExportedProperty(category="recents")
private boolean mDragRequested;
+ @ViewDebug.ExportedProperty(category="recents")
private boolean mIsDragging;
private float mDragSlop;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 19ac1e7..98bc92f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -25,6 +25,7 @@
import android.util.ArraySet;
import android.util.FloatProperty;
import android.util.Property;
+import android.view.ViewDebug;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -216,15 +217,20 @@
private TaskStackLayoutAlgorithmCallbacks mCb;
// The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot.
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mTaskRect = new Rect();
// The freeform workspace bounds, inset from the top by the search bar, and is a fixed height
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mFreeformRect = new Rect();
// The stack bounds, inset from the top by the search bar, and runs to
// the bottom of the screen
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mStackRect = new Rect();
// This is the current system insets
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mSystemInsets = new Rect();
// This is the bounds of the history button above the stack rect
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mHistoryButtonRect = new Rect();
// The visible ranges when the stack is focused and unfocused
@@ -232,14 +238,17 @@
private Range mFocusedRange;
// The offset from the top when scrolled to the top of the stack
+ @ViewDebug.ExportedProperty(category="recents")
private int mFocusedPeekHeight;
// The offset from the top of the stack to the top of the bounds when the stack is scrolled to
// the end
+ @ViewDebug.ExportedProperty(category="recents")
private int mStackTopOffset;
// The offset from the bottom of the stack to the bottom of the bounds when the stack is
// scrolled to the front
+ @ViewDebug.ExportedProperty(category="recents")
private int mStackBottomOffset;
// The paths defining the motion of the tasks when the stack is focused and unfocused
@@ -250,27 +259,36 @@
// The state of the stack focus (0..1), which controls the transition of the stack from the
// focused to non-focused state
+ @ViewDebug.ExportedProperty(category="recents")
private float mFocusState;
// The animator used to reset the focused state
private ObjectAnimator mFocusStateAnimator;
// The smallest scroll progress, at this value, the back most task will be visible
+ @ViewDebug.ExportedProperty(category="recents")
float mMinScrollP;
// The largest scroll progress, at this value, the front most task will be visible above the
// navigation bar
+ @ViewDebug.ExportedProperty(category="recents")
float mMaxScrollP;
// The initial progress that the scroller is set when you first enter recents
+ @ViewDebug.ExportedProperty(category="recents")
float mInitialScrollP;
// The task progress for the front-most task in the stack
+ @ViewDebug.ExportedProperty(category="recents")
float mFrontMostTaskP;
// The last computed task counts
+ @ViewDebug.ExportedProperty(category="recents")
int mNumStackTasks;
+ @ViewDebug.ExportedProperty(category="recents")
int mNumFreeformTasks;
// The min/max z translations
+ @ViewDebug.ExportedProperty(category="recents")
int mMinTranslationZ;
+ @ViewDebug.ExportedProperty(category="recents")
int mMaxTranslationZ;
// Optimization, allows for quick lookup of task -> index
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index fb3515a..9560eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -20,8 +20,6 @@
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.ComponentName;
@@ -40,11 +38,10 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
@@ -121,8 +118,11 @@
LayoutInflater mInflater;
TaskStack mStack;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
TaskStackLayoutAlgorithm mLayoutAlgorithm;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
TaskStackViewScroller mStackScroller;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
TaskStackViewTouchHandler mTouchHandler;
TaskStackAnimationHelper mAnimationHelper;
GradientDrawable mFreeformWorkspaceBackground;
@@ -134,25 +134,36 @@
ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
AnimationProps mDeferredTaskViewLayoutAnimation = null;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
DozeTrigger mUIDozeTrigger;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
Task mFocusedTask;
int mTaskCornerRadiusPx;
private int mDividerSize;
private int mStartTimerIndicatorDuration;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTaskViewsClipDirty = true;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mAwaitingFirstLayout = true;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mInMeasureLayout = false;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mEnterAnimationComplete = false;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTouchExplorationEnabled;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mScreenPinningEnabled;
// The stable stack bounds are the full bounds that we were measured with from RecentsView
+ @ViewDebug.ExportedProperty(category="recents")
private Rect mStableStackBounds = new Rect();
// The current stack bounds are dynamic and may change as the user drags and drops
+ @ViewDebug.ExportedProperty(category="recents")
private Rect mStackBounds = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
private int[] mTmpVisibleRange = new int[2];
private Rect mTmpRect = new Rect();
private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index d1bce55..b54ea1d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -23,6 +23,7 @@
import android.util.FloatProperty;
import android.util.Log;
import android.util.Property;
+import android.view.ViewDebug;
import android.widget.OverScroller;
import com.android.systemui.Interpolators;
@@ -63,6 +64,7 @@
TaskStackLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewScrollerCallbacks mCb;
+ @ViewDebug.ExportedProperty(category="recents")
float mStackScrollP;
float mFlingDownScrollP;
int mFlingDownY;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 5d1bb66f..55f9fba 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -28,6 +28,7 @@
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewDebug;
import android.view.ViewParent;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
@@ -69,6 +70,7 @@
FlingAnimationUtils mFlingAnimUtils;
ValueAnimator mScrollFlingAnimator;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mIsScrolling;
float mDownScrollP;
int mDownX, mDownY;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 850e36e7..0f485ac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -35,8 +35,10 @@
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
import android.view.animation.AccelerateInterpolator;
+import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
@@ -105,31 +107,46 @@
}
};
+ @ViewDebug.ExportedProperty(category="recents")
float mTaskProgress;
+ @ViewDebug.ExportedProperty(category="recents")
float mMaxDimScale;
+ @ViewDebug.ExportedProperty(category="recents")
int mDimAlpha;
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(3f);
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
Paint mDimLayerPaint = new Paint();
float mActionButtonTranslationZ;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
Task mTask;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTaskDataLoaded;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mClipViewInStack = true;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTouchExplorationEnabled;
+ @ViewDebug.ExportedProperty(category="recents")
+ boolean mIsDisabledInSafeMode;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="view_bounds_")
AnimateableViewBounds mViewBounds;
private AnimatorSet mTransformAnimation;
private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
View mContent;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
TaskViewThumbnail mThumbnailView;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
TaskViewHeader mHeaderView;
View mActionButtonView;
TaskViewCallbacks mCb;
+ @ViewDebug.ExportedProperty(category="recents")
Point mDownTouchPos = new Point();
+ private Toast mDisabledAppToast;
+
public TaskView(Context context) {
this(context, null);
}
@@ -549,15 +566,17 @@
/**** TaskCallbacks Implementation ****/
public void onTaskBound(Task t) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
mTask = t;
mTask.addCallback(this);
+ mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
}
@Override
public void onTaskDataLoaded(Task task) {
// Bind each of the views to the new task data
- mThumbnailView.rebindToTask(mTask);
- mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled);
+ mThumbnailView.rebindToTask(mTask, mIsDisabledInSafeMode);
+ mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
mTaskDataLoaded = true;
}
@@ -572,13 +591,24 @@
@Override
public void onTaskStackIdChanged() {
- mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled);
+ mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
}
/**** View.OnClickListener Implementation ****/
@Override
public void onClick(final View v) {
+ if (mIsDisabledInSafeMode) {
+ Context context = getContext();
+ String msg = context.getString(R.string.recents_launch_disabled_message, mTask.title);
+ if (mDisabledAppToast != null) {
+ mDisabledAppToast.cancel();
+ }
+ mDisabledAppToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
+ mDisabledAppToast.show();
+ return;
+ }
+
boolean screenPinningRequested = false;
if (v == mActionButtonView) {
// Reset the translation of the action button before we animate it out
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index c91a833..bb56a52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -36,6 +36,7 @@
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewAnimationUtils;
+import android.view.ViewDebug;
import android.view.ViewStub;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -74,6 +75,8 @@
private Paint mHighlightPaint = new Paint();
private Paint mBackgroundPaint = new Paint();
+ private int mColor;
+ private float mDimAlpha;
public HighlightColorDrawable() {
mBackgroundPaint.setColor(Color.argb(255, 0, 0, 0));
@@ -83,15 +86,19 @@
}
public void setColorAndDim(int color, float dimAlpha) {
- mBackgroundPaint.setColor(color);
+ if (mColor != color || Float.compare(mDimAlpha, dimAlpha) != 0) {
+ mColor = color;
+ mDimAlpha = dimAlpha;
+ mBackgroundPaint.setColor(color);
- ColorUtils.colorToHSL(color, mTmpHSL);
- // TODO: Consider using the saturation of the color to adjust the lightness as well
- mTmpHSL[2] = Math.min(1f,
- mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
- mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
+ ColorUtils.colorToHSL(color, mTmpHSL);
+ // TODO: Consider using the saturation of the color to adjust the lightness as well
+ mTmpHSL[2] = Math.min(1f,
+ mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
+ mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
- invalidateSelf();
+ invalidateSelf();
+ }
}
@Override
@@ -121,6 +128,10 @@
public int getOpacity() {
return PixelFormat.OPAQUE;
}
+
+ public int getColor() {
+ return mColor;
+ }
}
Task mTask;
@@ -139,9 +150,11 @@
ProgressBar mFocusTimerIndicator;
// Header drawables
+ @ViewDebug.ExportedProperty(category="recents")
Rect mTaskViewRect = new Rect();
int mCornerRadius;
int mHighlightHeight;
+ @ViewDebug.ExportedProperty(category="recents")
float mDimAlpha;
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
@@ -153,6 +166,7 @@
Drawable mDarkInfoIcon;
int mTaskBarViewLightTextColor;
int mTaskBarViewDarkTextColor;
+ int mDisabledTaskBarBackgroundColor;
int mMoveTaskTargetStackId = INVALID_STACK_ID;
// Header background
@@ -195,6 +209,8 @@
mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light);
mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark);
+ mDisabledTaskBarBackgroundColor =
+ context.getColor(R.color.recents_task_bar_disabled_background_color);
// Configure the background and dim
mBackground = new HighlightColorDrawable();
@@ -331,17 +347,17 @@
*/
void setDimAlpha(float dimAlpha) {
mDimAlpha = dimAlpha;
- updateBackgroundColor(dimAlpha);
+ updateBackgroundColor(mBackground.getColor(), dimAlpha);
}
/**
* Updates the background and highlight colors for this header.
*/
- private void updateBackgroundColor(float dimAlpha) {
+ private void updateBackgroundColor(int color, float dimAlpha) {
if (mTask != null) {
- mBackground.setColorAndDim(mTask.colorPrimary, dimAlpha);
+ mBackground.setColorAndDim(color, dimAlpha);
// TODO: Consider using the saturation of the color to adjust the lightness as well
- ColorUtils.colorToHSL(mTask.colorPrimary, mTmpHSL);
+ ColorUtils.colorToHSL(color, mTmpHSL);
mTmpHSL[2] = Math.min(1f, mTmpHSL[2] + OVERLAY_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
mOverlayBackground.setColorAndDim(ColorUtils.HSLToColor(mTmpHSL), dimAlpha);
mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
@@ -350,12 +366,15 @@
}
/** Binds the bar view to the task */
- public void rebindToTask(Task t, boolean touchExplorationEnabled) {
+ public void rebindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
mTask = t;
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
// otherwise, we fall back to the application icon
- updateBackgroundColor(mDimAlpha);
+ int primaryColor = disabledInSafeMode
+ ? mDisabledTaskBarBackgroundColor
+ : t.colorPrimary;
+ updateBackgroundColor(primaryColor, mDimAlpha);
if (t.icon != null) {
mIconView.setImageDrawable(t.icon);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index f90951e..0fec9c3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -21,13 +21,17 @@
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.LightingColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.Region;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewDebug;
import com.android.systemui.R;
import com.android.systemui.recents.model.Task;
@@ -39,27 +43,40 @@
*/
public class TaskViewThumbnail extends View {
+
+ private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
+ private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
+
private Task mTask;
// Drawing
+ @ViewDebug.ExportedProperty(category="recents")
Rect mThumbnailRect = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
Rect mTaskViewRect = new Rect();
int mCornerRadius;
+ @ViewDebug.ExportedProperty(category="recents")
float mDimAlpha;
Matrix mScaleMatrix = new Matrix();
Paint mDrawPaint = new Paint();
+ Paint mBgFillPaint = new Paint();
BitmapShader mBitmapShader;
LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
// Task bar clipping, the top of this thumbnail can be clipped against the opaque header
// bar that overlaps this thumbnail
View mTaskBar;
+ @ViewDebug.ExportedProperty(category="recents")
Rect mClipRect = new Rect();
// Visibility optimization, if the thumbnail height is less than the height of the header
// bar for the task view, then just mark this thumbnail view as invisible
+ @ViewDebug.ExportedProperty(category="recents")
boolean mInvisible;
+ @ViewDebug.ExportedProperty(category="recents")
+ boolean mDisabledInSafeMode;
+
public TaskViewThumbnail(Context context) {
this(context, null);
}
@@ -79,6 +96,7 @@
mDrawPaint.setAntiAlias(true);
mCornerRadius = getResources().getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius);
+ mBgFillPaint.setColor(Color.WHITE);
}
/**
@@ -100,10 +118,39 @@
if (mInvisible) {
return;
}
- // Draw the thumbnail with the rounded corners
- canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
- mCornerRadius,
- mCornerRadius, mDrawPaint);
+
+ int thumbnailHeight = (int) (((float) mTaskViewRect.width() / mThumbnailRect.width()) *
+ mThumbnailRect.height());
+ if (thumbnailHeight >= mTaskViewRect.height()) {
+ // The thumbnail fills the full task view bounds, so just draw it
+ canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
+ mCornerRadius, mCornerRadius, mDrawPaint);
+ } else {
+ int count = 0;
+ if (thumbnailHeight > 0) {
+ // The thumbnail only covers part of the task view bounds, so fill in the
+ // non-thumbnail space with the default background color. This is the equivalent of
+ // the GL border texture mode.
+ count = canvas.save();
+
+ // Since we only want the top corners to be rounded, draw slightly beyond the
+ // thumbnail height, but clip to the thumbnail height
+ canvas.clipRect(0, 0, mTaskViewRect.width(), thumbnailHeight, Region.Op.REPLACE);
+ canvas.drawRoundRect(0, 0, mTaskViewRect.width(), thumbnailHeight + mCornerRadius,
+ mCornerRadius, mCornerRadius, mDrawPaint);
+ }
+
+ // In the remaining space, draw the background color
+ canvas.clipRect(0, thumbnailHeight, mTaskViewRect.width(), mTaskViewRect.height(),
+ Region.Op.REPLACE);
+ canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
+ mTaskViewRect.width(), mTaskViewRect.height(), mCornerRadius, mCornerRadius,
+ mBgFillPaint);
+
+ if (thumbnailHeight > 0) {
+ canvas.restoreToCount(count);
+ }
+ }
}
/** Sets the thumbnail to a given bitmap. */
@@ -128,9 +175,27 @@
}
int mul = (int) ((1.0f - mDimAlpha) * 255);
if (mBitmapShader != null) {
- mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
- mDrawPaint.setColorFilter(mLightingColorFilter);
- mDrawPaint.setColor(0xffffffff);
+ if (mDisabledInSafeMode) {
+ // Brightness: C-new = C-old*(1-amount) + amount
+ TMP_FILTER_COLOR_MATRIX.setSaturation(0);
+ float scale = 1f - mDimAlpha;
+ float[] mat = TMP_BRIGHTNESS_COLOR_MATRIX.getArray();
+ mat[0] = scale;
+ mat[6] = scale;
+ mat[12] = scale;
+ mat[4] = mDimAlpha;
+ mat[9] = mDimAlpha;
+ mat[14] = mDimAlpha;
+ TMP_FILTER_COLOR_MATRIX.preConcat(TMP_BRIGHTNESS_COLOR_MATRIX);
+ ColorMatrixColorFilter filter = new ColorMatrixColorFilter(TMP_FILTER_COLOR_MATRIX);
+ mDrawPaint.setColorFilter(filter);
+ mBgFillPaint.setColorFilter(filter);
+ } else {
+ mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
+ mDrawPaint.setColorFilter(mLightingColorFilter);
+ mDrawPaint.setColor(0xFFffffff);
+ mBgFillPaint.setColorFilter(mLightingColorFilter);
+ }
} else {
int grey = mul;
mDrawPaint.setColorFilter(null);
@@ -196,10 +261,14 @@
}
/** Binds the thumbnail view to the task */
- void rebindToTask(Task t) {
+ void rebindToTask(Task t, boolean disabledInSafeMode) {
mTask = t;
+ mDisabledInSafeMode = disabledInSafeMode;
if (t.thumbnail != null) {
setThumbnail(t.thumbnail);
+ if (t.colorBackground != 0) {
+ mBgFillPaint.setColor(t.colorBackground);
+ }
} else {
setThumbnail(null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index c9ebfa0..2bebac2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -38,6 +38,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
@@ -116,7 +117,7 @@
ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
ExpandableNotificationRow.OnExpandClickListener {
public static final String TAG = "StatusBar";
- public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ public static final boolean DEBUG = false;
public static final boolean MULTIUSER_DEBUG = false;
public static final boolean ENABLE_REMOTE_INPUT =
@@ -228,6 +229,7 @@
protected int mState;
protected boolean mBouncerShowing;
protected boolean mShowLockscreenNotifications;
+ protected boolean mAllowLockscreenRemoteInput;
protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
protected DismissView mDismissView;
@@ -399,11 +401,26 @@
}
p = p.getParent();
}
+ ExpandableNotificationRow row = null;
+ while (p != null) {
+ if (p instanceof ExpandableNotificationRow) {
+ row = (ExpandableNotificationRow) p;
+ break;
+ }
+ p = p.getParent();
+ }
- if (riv == null) {
+ if (riv == null || row == null) {
return false;
}
+ row.setUserExpanded(true);
+
+ if (isLockscreenPublicMode() && !mAllowLockscreenRemoteInput) {
+ onLockedRemoteInput(row, view);
+ return true;
+ }
+
riv.setVisibility(View.VISIBLE);
int cx = view.getLeft() + view.getWidth() / 2;
int cy = view.getTop() + view.getHeight() / 2;
@@ -618,6 +635,10 @@
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
mSettingsObserver,
UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), false,
+ mSettingsObserver,
+ UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -641,12 +662,15 @@
// Connect in to the status bar manager service
mCommandQueue = new CommandQueue(this);
- int[] switches = new int[8];
+ int[] switches = new int[9];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
ArrayList<String> iconSlots = new ArrayList<>();
ArrayList<StatusBarIcon> icons = new ArrayList<>();
+ Rect fullscreenStackBounds = new Rect();
+ Rect dockedStackBounds = new Rect();
try {
- mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders);
+ mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
+ fullscreenStackBounds, dockedStackBounds);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
@@ -655,7 +679,8 @@
mSettingsObserver.onChange(false); // set up
disable(switches[0], switches[6], false /* animate */);
- setSystemUiVisibility(switches[1], 0xffffffff);
+ setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
+ fullscreenStackBounds, dockedStackBounds);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
@@ -1295,6 +1320,8 @@
}
}
+ protected void onLockedRemoteInput(ExpandableNotificationRow row, View clickedView) {}
+
@Override
public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
}
@@ -1931,6 +1958,10 @@
mShowLockscreenNotifications = show;
}
+ protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
+ mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
+ }
+
private void updateLockscreenNotificationSetting() {
final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
@@ -1940,7 +1971,14 @@
null /* admin */, mCurrentUserId);
final boolean allowedByDpm = (dpmFlags
& DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+
+ final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
+ 0,
+ mCurrentUserId) != 0;
+
setShowLockscreenNotifications(show && allowedByDpm);
+ setLockScreenAllowRemoteInput(remoteInput);
}
protected abstract void setAreThereNotifications();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 71347c4..3b960ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -17,12 +17,14 @@
package com.android.systemui.statusbar;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Pair;
+import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
@@ -94,7 +96,8 @@
public void animateExpandNotificationsPanel();
public void animateCollapsePanels(int flags);
public void animateExpandSettingsPanel(String obj);
- public void setSystemUiVisibility(int vis, int mask);
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis,
+ int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds);
public void topAppWindowChanged(boolean visible);
public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
@@ -169,11 +172,19 @@
}
}
- public void setSystemUiVisibility(int vis, int mask) {
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
synchronized (mLock) {
// Don't coalesce these, since it might have one time flags set such as
// STATUS_BAR_UNHIDE which might get lost.
- mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget();
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = vis;
+ args.argi2 = fullscreenStackVis;
+ args.argi3 = dockedStackVis;
+ args.argi4 = mask;
+ args.arg1 = fullscreenStackBounds;
+ args.arg2 = dockedStackBounds;
+ mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
}
}
@@ -377,7 +388,10 @@
mCallbacks.animateExpandSettingsPanel((String) msg.obj);
break;
case MSG_SET_SYSTEMUI_VISIBILITY:
- mCallbacks.setSystemUiVisibility(msg.arg1, msg.arg2);
+ SomeArgs args = (SomeArgs) msg.obj;
+ mCallbacks.setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
+ args.argi4, (Rect) args.arg1, (Rect) args.arg2);
+ args.recycle();
break;
case MSG_TOP_APP_WINDOW_CHANGED:
mCallbacks.topAppWindowChanged(msg.arg1 != 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 264655c..7422902e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -527,6 +527,8 @@
mGuts.setVisibility(oldGuts.getVisibility());
addView(mGuts, index);
}
+ mPrivateLayout.reInflateViews();
+ mPublicLayout.reInflateViews();
}
public interface ExpansionLogger {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 76e522e..d5361dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -248,14 +248,16 @@
public void reset(boolean resetActualHeight) {
if (mContractedChild != null) {
mContractedChild.animate().cancel();
+ removeView(mContractedChild);
}
if (mExpandedChild != null) {
mExpandedChild.animate().cancel();
+ removeView(mExpandedChild);
}
if (mHeadsUpChild != null) {
mHeadsUpChild.animate().cancel();
+ removeView(mHeadsUpChild);
}
- removeAllViews();
mContractedChild = null;
mExpandedChild = null;
mHeadsUpChild = null;
@@ -494,7 +496,8 @@
return VISIBLE_TYPE_EXPANDED;
}
} else {
- if (viewHeight <= mContractedChild.getHeight() || noExpandedChild) {
+ if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
+ && (!mIsChildInGroup || !mContainingNotification.isExpanded()))) {
return VISIBLE_TYPE_CONTRACTED;
} else {
return VISIBLE_TYPE_EXPANDED;
@@ -569,6 +572,9 @@
if (mIsChildInGroup) {
mSingleLineView = mHybridViewManager.bindFromNotification(
mSingleLineView, mStatusBarNotification.getNotification());
+ } else if (mSingleLineView != null) {
+ removeView(mSingleLineView);
+ mSingleLineView = null;
}
}
@@ -685,4 +691,12 @@
public void requestSelectLayout(boolean needsAnimation) {
selectLayout(needsAnimation, false);
}
+
+ public void reInflateViews() {
+ if (mIsChildInGroup && mSingleLineView != null) {
+ removeView(mSingleLineView);
+ mSingleLineView = null;
+ updateSingleLineView();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index dd6d6f3..7346bec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -113,15 +113,15 @@
? new Notification.Topic(Notification.TOPIC_DEFAULT, mContext.getString(
com.android.internal.R.string.default_notification_topic_label))
: sbn.getNotification().getTopic();
- boolean doesAppUseTopics = false;
+ boolean doesUserUseTopics = false;
try {
- doesAppUseTopics =
- mINotificationManager.doesAppUseTopics(sbn.getPackageName(), sbn.getUid());
+ doesUserUseTopics =
+ mINotificationManager.doesUserUseTopics(sbn.getPackageName(), sbn.getUid());
} catch (RemoteException e) {}
- final boolean appUsesTopics = doesAppUseTopics;
+ final boolean userUsesTopics = doesUserUseTopics;
mApplyToTopic = (RadioButton) row.findViewById(R.id.apply_to_topic);
- if (appUsesTopics) {
+ if (userUsesTopics) {
mApplyToTopic.setChecked(true);
}
final View applyToApp = row.findViewById(R.id.apply_to_app);
@@ -156,7 +156,7 @@
updateTitleAndSummary(progress);
if (fromUser) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_MODIFY_IMPORTANCE_SLIDER);
- if (appUsesTopics) {
+ if (userUsesTopics) {
mApplyToTopic.setVisibility(View.VISIBLE);
mApplyToTopic.setText(
mContext.getString(R.string.apply_to_topic, mTopic.getLabel()));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 6801e5f..9aa5ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -21,6 +21,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.telephony.SubscriptionInfo;
@@ -80,6 +81,7 @@
private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>();
private int mIconTint = Color.WHITE;
private float mDarkIntensity;
+ private final Rect mTintArea = new Rect();
ViewGroup mEthernetGroup, mWifiGroup;
View mNoSimsCombo;
@@ -490,23 +492,31 @@
}
}
- public void setIconTint(int tint, float darkIntensity) {
- boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity;
+ public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
+ boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity
+ || !mTintArea.equals(tintArea);
mIconTint = tint;
mDarkIntensity = darkIntensity;
+ mTintArea.set(tintArea);
if (changed && isAttachedToWindow()) {
applyIconTint();
}
}
private void applyIconTint() {
- setTint(mVpn, mIconTint);
- setTint(mAirplane, mIconTint);
- applyDarkIntensity(mDarkIntensity, mNoSims, mNoSimsDark);
- applyDarkIntensity(mDarkIntensity, mWifi, mWifiDark);
- applyDarkIntensity(mDarkIntensity, mEthernet, mEthernetDark);
+ setTint(mVpn, StatusBarIconController.getTint(mTintArea, mVpn, mIconTint));
+ setTint(mAirplane, StatusBarIconController.getTint(mTintArea, mAirplane, mIconTint));
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(mTintArea, mNoSims, mDarkIntensity),
+ mNoSims, mNoSimsDark);
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(mTintArea, mWifi, mDarkIntensity),
+ mWifi, mWifiDark);
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(mTintArea, mEthernet, mDarkIntensity),
+ mEthernet, mEthernetDark);
for (int i = 0; i < mPhoneStates.size(); i++) {
- mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity);
+ mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity, mTintArea);
}
}
@@ -613,9 +623,11 @@
}
}
- public void setIconTint(int tint, float darkIntensity) {
- applyDarkIntensity(darkIntensity, mMobile, mMobileDark);
- setTint(mMobileType, tint);
+ public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(tintArea, mMobile, darkIntensity),
+ mMobile, mMobileDark);
+ setTint(mMobileType, StatusBarIconController.getTint(tintArea, mMobileType, tint));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index 60c1911..aa001ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -45,4 +45,10 @@
mInvertHelper.update(dark);
}
}
+
+ @Override
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ mView.setAlpha(visible ? 1.0f : 0.0f);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index a2b4c5d..328f8b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -105,6 +105,7 @@
@Override
public void setVisible(boolean visible) {
+ mView.animate().cancel();
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 5832d86..67d31be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -293,14 +293,19 @@
mTransformedView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
mTransformedView.setAlpha(visible ? 1.0f : 0.0f);
if (visible) {
- mTransformedView.setTranslationX(0);
- mTransformedView.setTranslationY(0);
- mTransformedView.setScaleX(1.0f);
- mTransformedView.setScaleY(1.0f);
+ resetTransformedView();
}
}
public void prepareFadeIn() {
+ resetTransformedView();
+ }
+
+ private void resetTransformedView() {
+ mTransformedView.setTranslationX(0);
+ mTransformedView.setTranslationY(0);
+ mTransformedView.setScaleX(1.0f);
+ mTransformedView.setScaleY(1.0f);
}
public static TransformState obtain() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
new file mode 100644
index 0000000..f98b9e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
@@ -0,0 +1,126 @@
+/*
+ * 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.phone;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+
+/**
+ * Controls how light status bar flag applies to the icons.
+ */
+public class LightStatusBarController {
+
+ private final StatusBarIconController mIconController;
+ private final BatteryController mBatteryController;
+ private FingerprintUnlockController mFingerprintUnlockController;
+
+ private int mFullscreenStackVisibility;
+ private int mDockedStackVisibility;
+ private boolean mFullscreenLight;
+ private boolean mDockedLight;
+
+ private final Rect mLastFullscreenBounds = new Rect();
+ private final Rect mLastDockedBounds = new Rect();
+
+ public LightStatusBarController(StatusBarIconController iconController,
+ BatteryController batteryController) {
+ mIconController = iconController;
+ mBatteryController = batteryController;
+ }
+
+ public void setFingerprintUnlockController(
+ FingerprintUnlockController fingerprintUnlockController) {
+ mFingerprintUnlockController = fingerprintUnlockController;
+ }
+
+ public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis, int mask,
+ Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,
+ int statusBarMode) {
+ int oldFullscreen = mFullscreenStackVisibility;
+ int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);
+ int diffFullscreen = newFullscreen ^ oldFullscreen;
+ int oldDocked = mDockedStackVisibility;
+ int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask);
+ int diffDocked = newDocked ^ oldDocked;
+ if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+ || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+ || sbModeChanged
+ || !mLastFullscreenBounds.equals(fullscreenStackBounds)
+ || !mLastDockedBounds.equals(dockedStackBounds)) {
+
+ mFullscreenLight = isLight(newFullscreen, statusBarMode);
+ mDockedLight = isLight(newDocked, statusBarMode);
+ update(fullscreenStackBounds, dockedStackBounds);
+ }
+ mFullscreenStackVisibility = newFullscreen;
+ mDockedStackVisibility = newDocked;
+ mLastFullscreenBounds.set(fullscreenStackBounds);
+ mLastDockedBounds.set(dockedStackBounds);
+ }
+
+ private boolean isLight(int vis, int statusBarMode) {
+ boolean isTransparentBar = (statusBarMode == MODE_TRANSPARENT
+ || statusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
+ boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
+ boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
+ return allowLight && light;
+ }
+
+ private boolean animateChange() {
+ if (mFingerprintUnlockController == null) {
+ return false;
+ }
+ int unlockMode = mFingerprintUnlockController.getMode();
+ return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+ }
+
+ private void update(Rect fullscreenStackBounds, Rect dockedStackBounds) {
+ boolean hasDockedStack = !dockedStackBounds.isEmpty();
+
+ // If both are light or fullscreen is light and there is no docked stack, all icons get
+ // dark.
+ if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) {
+ mIconController.setIconsDarkArea(null);
+ mIconController.setIconsDark(true, animateChange());
+
+ }
+
+ // If no one is light or the fullscreen is not light and there is no docked stack,
+ // all icons become white.
+ else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) {
+ mIconController.setIconsDark(false, animateChange());
+
+ }
+
+ // Not the same for every stack, magic!
+ else {
+ Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds;
+ if (bounds.isEmpty()) {
+ mIconController.setIconsDarkArea(null);
+ } else {
+ mIconController.setIconsDarkArea(bounds);
+ }
+ mIconController.setIconsDark(true, animateChange());
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 03a597c..6e345f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -4,6 +4,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
@@ -32,6 +33,7 @@
protected View mNotificationIconArea;
private IconMerger mNotificationIcons;
private ImageView mMoreIcon;
+ private final Rect mTintArea = new Rect();
public NotificationIconAreaController(Context context, PhoneStatusBar phoneStatusBar) {
mPhoneStatusBar = phoneStatusBar;
@@ -67,6 +69,20 @@
}
/**
+ * See {@link StatusBarIconController#setIconsDarkArea}.
+ *
+ * @param tintArea the area in which to tint the icons, specified in screen coordinates
+ */
+ public void setTintArea(Rect tintArea) {
+ if (tintArea == null) {
+ mTintArea.setEmpty();
+ } else {
+ mTintArea.set(tintArea);
+ }
+ applyNotificationIconsTint();
+ }
+
+ /**
* Sets the color that should be used to tint any icons in the notification area. If this
* method is not called, the default tint is {@link Color#WHITE}.
*/
@@ -145,7 +161,8 @@
boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
if (colorize) {
- v.setImageTintList(ColorStateList.valueOf(mIconTint));
+ v.setImageTintList(ColorStateList.valueOf(
+ StatusBarIconController.getTint(mTintArea, v, mIconTint)));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 14376ac..278dfe6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -69,6 +69,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
@@ -88,6 +89,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
import android.view.ViewStub;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -296,6 +298,7 @@
BrightnessMirrorController mBrightnessMirrorController;
AccessibilityController mAccessibilityController;
FingerprintUnlockController mFingerprintUnlockController;
+ LightStatusBarController mLightStatusBarController;
int mNaturalBarHeight = -1;
@@ -340,6 +343,9 @@
private long mKeyguardFadingAwayDelay;
private long mKeyguardFadingAwayDuration;
+ // RemoteInputView to be activated after unlock
+ private View mPendingRemoteInputView;
+
int mMaxAllowedKeyguardNotifications;
boolean mExpandedVisible;
@@ -361,6 +367,8 @@
// tracking calls to View.setSystemUiVisibility()
int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+ private final Rect mLastFullscreenStackBounds = new Rect();
+ private final Rect mLastDockedStackBounds = new Rect();
// last value sent to window manager
private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
@@ -844,6 +852,8 @@
mAccessibilityController = new AccessibilityController(mContext);
mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
mNextAlarmController = new NextAlarmController(mContext);
+ mLightStatusBarController = new LightStatusBarController(mIconController,
+ mBatteryController);
mKeyguardMonitor = new KeyguardMonitor(mContext);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,
@@ -1089,6 +1099,7 @@
mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
+ mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController);
}
@Override
@@ -1500,8 +1511,8 @@
}
private void updateNotificationShadeForChildren() {
+ // First let's remove all children which don't belong in the parents
ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
- boolean orderChanged = false;
for (int i = 0; i < mStackScroller.getChildCount(); i++) {
View view = mStackScroller.getChildAt(i);
if (!(view instanceof ExpandableNotificationRow)) {
@@ -1513,7 +1524,6 @@
List<ExpandableNotificationRow> children = parent.getNotificationChildren();
List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
- // lets first remove all undesired children
if (children != null) {
toRemove.clear();
for (ExpandableNotificationRow childRow : children) {
@@ -1526,8 +1536,21 @@
mStackScroller.notifyGroupChildRemoved(remove);
}
}
+ }
- // We now add all the children which are not in there already
+ // Let's now add all notification children which are missing
+ boolean orderChanged = false;
+ for (int i = 0; i < mStackScroller.getChildCount(); i++) {
+ View view = mStackScroller.getChildAt(i);
+ if (!(view instanceof ExpandableNotificationRow)) {
+ // We don't care about non-notification views.
+ continue;
+ }
+
+ ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
+ List<ExpandableNotificationRow> children = parent.getNotificationChildren();
+ List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
+
for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
childIndex++) {
ExpandableNotificationRow childView = orderedChildren.get(childIndex);
@@ -2238,7 +2261,7 @@
@Override
public void maybeEscalateHeadsUp() {
- TreeSet<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getSortedEntries();
+ Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries();
for (HeadsUpManager.HeadsUpEntry entry : entries) {
final StatusBarNotification sbn = entry.entry.notification;
final Notification notification = sbn.getNotification();
@@ -2524,7 +2547,8 @@
}
@Override // CommandQueue
- public void setSystemUiVisibility(int vis, int mask) {
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
final int oldVal = mSystemUiVisibility;
final int newVal = (oldVal&~mask) | (vis&mask);
final int diff = newVal ^ oldVal;
@@ -2533,6 +2557,7 @@
Integer.toHexString(vis), Integer.toHexString(mask),
Integer.toHexString(oldVal), Integer.toHexString(newVal),
Integer.toHexString(diff)));
+ boolean sbModeChanged = false;
if (diff != 0) {
// we never set the recents bit via this method, so save the prior state to prevent
// clobbering the bit below
@@ -2566,7 +2591,7 @@
oldVal, newVal, mNavigationBarView.getBarTransitions(),
View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
View.NAVIGATION_BAR_TRANSPARENT);
- final boolean sbModeChanged = sbMode != -1;
+ sbModeChanged = sbMode != -1;
final boolean nbModeChanged = nbMode != -1;
boolean checkBarModes = false;
if (sbModeChanged && sbMode != mStatusBarMode) {
@@ -2593,18 +2618,6 @@
mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
}
- if ((diff & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 || sbModeChanged) {
- boolean isTransparentBar = (mStatusBarMode == MODE_TRANSPARENT
- || mStatusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
- boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
- boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
- boolean animate = mFingerprintUnlockController == null
- || (mFingerprintUnlockController.getMode()
- != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
- && mFingerprintUnlockController.getMode()
- != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
- mIconController.setIconsDark(allowLight && light, animate);
- }
// restore the recents bit
if (wasRecentsVisible) {
mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
@@ -2613,6 +2626,9 @@
// send updated sysui visibility to window manager
notifyUiVisibilityChanged(mSystemUiVisibility);
}
+
+ mLightStatusBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
+ mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
}
private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
@@ -2740,9 +2756,12 @@
public void setLightsOn(boolean on) {
Log.v(TAG, "setLightsOn(" + on + ")");
if (on) {
- setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ setSystemUiVisibility(0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
+ mLastFullscreenStackBounds, mLastDockedStackBounds);
} else {
- setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
+ View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
+ mLastDockedStackBounds);
}
}
@@ -3564,6 +3583,7 @@
mDraggedDownRow.notifyHeightChanged(false /* needsAnimation */);
mDraggedDownRow = null;
}
+ mPendingRemoteInputView = null;
mAssistManager.onLockscreenShown();
}
@@ -3680,6 +3700,7 @@
public boolean hideKeyguard() {
boolean staying = mLeaveOpenOnKeyguardHide;
setBarState(StatusBarState.SHADE);
+ View viewToClick = null;
if (mLeaveOpenOnKeyguardHide) {
mLeaveOpenOnKeyguardHide = false;
long delay = calculateGoingToFullShadeDelay();
@@ -3688,6 +3709,8 @@
mDraggedDownRow.setUserLocked(false);
mDraggedDownRow = null;
}
+ viewToClick = mPendingRemoteInputView;
+ mPendingRemoteInputView = null;
// Disable layout transitions in navbar for this transition because the load is just
// too heavy for the CPU and GPU on any device.
@@ -3705,6 +3728,10 @@
}
updateKeyguardState(staying, false /* fromShadeLocked */);
+ if (viewToClick != null) {
+ viewToClick.callOnClick();
+ }
+
// Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile
// visibilities so next time we open the panel we know the correct height already.
if (mQSPanel != null) {
@@ -4068,6 +4095,7 @@
mLeaveOpenOnKeyguardHide = true;
showBouncer();
mDraggedDownRow = row;
+ mPendingRemoteInputView = null;
} else {
mNotificationPanel.animateToFullShade(0 /* delay */);
setBarState(StatusBarState.SHADE_LOCKED);
@@ -4076,6 +4104,13 @@
}
@Override
+ protected void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
+ mLeaveOpenOnKeyguardHide = true;
+ showBouncer();
+ mPendingRemoteInputView = clicked;
+ }
+
+ @Override
public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
if (mState == StatusBarState.KEYGUARD && nowExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 45aae2d..f61f31e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -22,6 +22,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Handler;
@@ -58,8 +59,8 @@
public class StatusBarIconController extends StatusBarIconList implements Tunable {
public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
-
public static final String ICON_BLACKLIST = "icon_blacklist";
+ public static final int DEFAULT_ICON_TINT = Color.WHITE;
private Context mContext;
private PhoneStatusBar mPhoneStatusBar;
@@ -79,8 +80,11 @@
private int mIconSize;
private int mIconHPadding;
- private int mIconTint = Color.WHITE;
+ private int mIconTint = DEFAULT_ICON_TINT;
private float mDarkIntensity;
+ private final Rect mTintArea = new Rect();
+ private static final Rect sTmpRect = new Rect();
+ private static final int[] sTmpInt2 = new int[2];
private boolean mTransitionPending;
private boolean mTintChangePending;
@@ -395,6 +399,25 @@
}
}
+ /**
+ * Sets the dark area so {@link #setIconsDark} only affects the icons in the specified area.
+ *
+ * @param darkArea the area in which icons should change it's tint, in logical screen
+ * coordinates
+ */
+ public void setIconsDarkArea(Rect darkArea) {
+ if (darkArea == null && mTintArea.isEmpty()) {
+ return;
+ }
+ if (darkArea == null) {
+ mTintArea.setEmpty();
+ } else {
+ mTintArea.set(darkArea);
+ }
+ applyIconTint();
+ mNotificationIconAreaController.setTintArea(darkArea);
+ }
+
public void setIconsDark(boolean dark, boolean animate) {
if (!animate) {
setIconTintInternal(dark ? 1.0f : 0.0f);
@@ -446,14 +469,60 @@
mPendingDarkIntensity = darkIntensity;
}
+ /**
+ * @return the tint to apply to {@param view} depending on the desired tint {@param color} and
+ * the screen {@param tintArea} in which to apply that tint
+ */
+ public static int getTint(Rect tintArea, View view, int color) {
+ if (isInArea(tintArea, view)) {
+ return color;
+ } else {
+ return DEFAULT_ICON_TINT;
+ }
+ }
+
+ /**
+ * @return the dark intensity to apply to {@param view} depending on the desired dark
+ * {@param intensity} and the screen {@param tintArea} in which to apply that intensity
+ */
+ public static float getDarkIntensity(Rect tintArea, View view, float intensity) {
+ if (isInArea(tintArea, view)) {
+ return intensity;
+ } else {
+ return 0f;
+ }
+ }
+
+ /**
+ * @return true if more than half of the {@param view} area are in {@param area}, false
+ * otherwise
+ */
+ private static boolean isInArea(Rect area, View view) {
+ if (area.isEmpty()) {
+ return true;
+ }
+ sTmpRect.set(area);
+ view.getLocationOnScreen(sTmpInt2);
+ int left = sTmpInt2[0];
+
+ int intersectStart = Math.max(left, area.left);
+ int intersectEnd = Math.min(left + view.getWidth(), area.right);
+ int intersectAmount = Math.max(0, intersectEnd - intersectStart);
+
+ boolean coversFullStatusBar = area.top <= 0;
+ boolean majorityOfWidth = 2 * intersectAmount > view.getWidth();
+ return majorityOfWidth && coversFullStatusBar;
+ }
+
private void applyIconTint() {
for (int i = 0; i < mStatusIcons.getChildCount(); i++) {
StatusBarIconView v = (StatusBarIconView) mStatusIcons.getChildAt(i);
- v.setImageTintList(ColorStateList.valueOf(mIconTint));
+ v.setImageTintList(ColorStateList.valueOf(getTint(mTintArea, v, mIconTint)));
}
- mSignalCluster.setIconTint(mIconTint, mDarkIntensity);
- mBatteryMeterView.setDarkIntensity(mDarkIntensity);
- mClock.setTextColor(mIconTint);
+ mSignalCluster.setIconTint(mIconTint, mDarkIntensity, mTintArea);
+ mBatteryMeterView.setDarkIntensity(
+ isInArea(mTintArea, mBatteryMeterView) ? mDarkIntensity : 0);
+ mClock.setTextColor(getTint(mTintArea, mClock, mIconTint));
}
public void appTransitionPending() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index f065522..ab81712 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -39,10 +39,10 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Stack;
-import java.util.TreeSet;
/**
* A manager which handles heads up notifications which is a special mode where
@@ -90,7 +90,6 @@
private int mSnoozeLengthMs;
private ContentObserver mSettingsObserver;
private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
- private TreeSet<HeadsUpEntry> mSortedEntries = new TreeSet<>();
private HashSet<String> mSwipedOutKeys = new HashSet<>();
private int mUser;
private Clock mClock;
@@ -230,7 +229,6 @@
private void removeHeadsUpEntry(NotificationData.Entry entry) {
HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
- mSortedEntries.remove(remove);
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
entry.row.setHeadsUp(false);
setEntryPinned(remove, false /* isPinned */);
@@ -345,12 +343,21 @@
return mHeadsUpEntries.get(key).entry;
}
- public TreeSet<HeadsUpEntry> getSortedEntries() {
- return mSortedEntries;
+ public Collection<HeadsUpEntry> getAllEntries() {
+ return mHeadsUpEntries.values();
}
public HeadsUpEntry getTopEntry() {
- return mSortedEntries.isEmpty() ? null : mSortedEntries.first();
+ if (mHeadsUpEntries.isEmpty()) {
+ return null;
+ }
+ HeadsUpEntry topEntry = null;
+ for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
+ if (topEntry == null || entry.compareTo(topEntry) == -1) {
+ topEntry = entry;
+ }
+ }
+ return topEntry;
}
/**
@@ -374,26 +381,18 @@
return;
}
if (mHasPinnedNotification) {
- int minX = 0;
- int maxX = 0;
- int maxY = 0;
- for (HeadsUpEntry entry : mSortedEntries) {
- ExpandableNotificationRow row = entry.entry.row;
- if (row.isPinned()) {
- if (row.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(row.getStatusBarNotification());
- if (groupSummary != null) {
- row = groupSummary;
- }
- }
- row.getLocationOnScreen(mTmpTwoArray);
- minX = mTmpTwoArray[0];
- maxX = mTmpTwoArray[0] + row.getWidth();
- maxY = row.getIntrinsicHeight();
- break;
+ ExpandableNotificationRow topEntry = getTopEntry().entry.row;
+ if (topEntry.isChildInGroup()) {
+ final ExpandableNotificationRow groupSummary
+ = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+ if (groupSummary != null) {
+ topEntry = groupSummary;
}
}
+ topEntry.getLocationOnScreen(mTmpTwoArray);
+ int minX = mTmpTwoArray[0];
+ int maxX = mTmpTwoArray[0] + topEntry.getWidth();
+ int maxY = topEntry.getIntrinsicHeight();
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
info.touchableRegion.set(minX, 0, maxX, maxY);
@@ -413,7 +412,7 @@
pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
pw.print(" now="); pw.println(SystemClock.elapsedRealtime());
pw.print(" mUser="); pw.println(mUser);
- for (HeadsUpEntry entry: mSortedEntries) {
+ for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
pw.print(" HeadsUpEntry="); pw.println(entry.entry);
}
int N = mSnoozedPackages.size();
@@ -633,7 +632,6 @@
}
public void updateEntry(boolean updatePostTime) {
- mSortedEntries.remove(HeadsUpEntry.this);
long currentTime = mClock.currentTimeMillis();
earliestRemovaltime = currentTime + mMinimumDisplayTime;
if (updatePostTime) {
@@ -648,7 +646,6 @@
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
}
- mSortedEntries.add(HeadsUpEntry.this);
}
private boolean isSticky() {
@@ -658,6 +655,13 @@
@Override
public int compareTo(HeadsUpEntry o) {
+ boolean isPinned = entry.row.isPinned();
+ boolean otherPinned = o.entry.row.isPinned();
+ if (isPinned && !otherPinned) {
+ return -1;
+ } else if (!isPinned && otherPinned) {
+ return 1;
+ }
boolean selfFullscreen = hasFullScreenIntent(entry);
boolean otherFullscreen = hasFullScreenIntent(o.entry);
if (selfFullscreen && !otherFullscreen) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 784f610..110258c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.tv;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.IBinder;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -68,7 +69,8 @@
}
@Override
- public void setSystemUiVisibility(int vis, int mask) {
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index b79a7ba..ad70853 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -110,9 +110,25 @@
// The minimal score for accepting a predicted gesture.
private static final float MIN_PREDICTION_SCORE = 2.0f;
+ // Distance a finger must travel before we decide if it is a gesture or not.
private static final int GESTURE_CONFIRM_MM = 10;
- private static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 1000;
- private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 500;
+
+ // Time threshold used to determine if an interaction is a gesture or not.
+ // If the first movement of 1cm takes longer than this value, we assume it's
+ // a slow movement, and therefore not a gesture.
+ //
+ // This value was determined by measuring the time for the first 1cm
+ // movement when gesturing, and touch exploring. Based on user testing,
+ // all gestures started with the initial movement taking less than 100ms.
+ // When touch exploring, the first movement almost always takes longer than
+ // 200ms. From this data, 150ms seems the best value to decide what
+ // kind of interaction it is.
+ private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
+
+ // Time threshold used to determine if a gesture should be cancelled. If
+ // the finger pauses for longer than this delay, the ongoing gesture is
+ // cancelled.
+ private static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 500;
AccessibilityGestureDetector(Context context, Listener listener) {
mListener = listener;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 7149065..f8bf59d2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -87,6 +87,7 @@
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.StringBuilderPrinter;
@@ -142,6 +143,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
@@ -786,8 +788,9 @@
case MSG_OP_COMPLETE:
{
try {
- BackupRestoreTask task = (BackupRestoreTask) msg.obj;
- task.operationComplete(msg.arg1);
+ Pair<BackupRestoreTask, Long> taskWithResult =
+ (Pair<BackupRestoreTask, Long>) msg.obj;
+ taskWithResult.first.operationComplete(taskWithResult.second);
} catch (ClassCastException e) {
Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
}
@@ -2460,7 +2463,7 @@
void execute();
// An operation that wanted a callback has completed
- void operationComplete(int result);
+ void operationComplete(long result);
// An operation that wanted a callback has timed out
void handleTimeout();
@@ -3095,7 +3098,7 @@
}
@Override
- public void operationComplete(int unusedResult) {
+ public void operationComplete(long unusedResult) {
// The agent reported back to us!
if (mBackupData == null) {
@@ -3494,7 +3497,7 @@
*/
int preflightFullBackup(PackageInfo pkg, IBackupAgent agent);
- long expectedSize();
+ long getExpectedSizeOrErrorCode();
};
class FullBackupEngine {
@@ -4532,7 +4535,7 @@
// a standalone thread. The runner owns this half of the pipe, and closes
// it to indicate EOD to the other end.
class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
- final AtomicInteger mResult = new AtomicInteger();
+ final AtomicLong mResult = new AtomicLong();
final CountDownLatch mLatch = new CountDownLatch(1);
final IBackupTransport mTransport;
@@ -4554,7 +4557,11 @@
// now wait to get our result back
mLatch.await();
- int totalSize = mResult.get();
+ long totalSize = mResult.get();
+ // If preflight timeouted, mResult will contain error code as int.
+ if (totalSize < 0) {
+ return (int) totalSize;
+ }
if (MORE_DEBUG) {
Slog.v(TAG, "Got preflight response; size=" + totalSize);
}
@@ -4581,7 +4588,7 @@
}
@Override
- public void operationComplete(int result) {
+ public void operationComplete(long result) {
// got the callback, and our preflightFullBackup() method is waiting for the result
if (MORE_DEBUG) {
Slog.i(TAG, "Preflight op complete, result=" + result);
@@ -4600,7 +4607,7 @@
}
@Override
- public long expectedSize() {
+ public long getExpectedSizeOrErrorCode() {
try {
mLatch.await();
return mResult.get();
@@ -4649,7 +4656,7 @@
}
long expectedSize() {
- return mPreflight.expectedSize();
+ return mPreflight.getExpectedSizeOrErrorCode();
}
}
}
@@ -8558,7 +8565,7 @@
}
@Override
- public void operationComplete(int unusedResult) {
+ public void operationComplete(long unusedResult) {
if (MORE_DEBUG) {
Slog.i(TAG, "operationComplete() during restore: target="
+ mCurrentPackage.packageName
@@ -9643,9 +9650,8 @@
// The completion callback, if any, is invoked on the handler
if (op != null && op.callback != null) {
- Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
- // NB: this cannot distinguish between results > 2 gig
- msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result;
+ Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
+ Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
mBackupHandler.sendMessage(msg);
}
}
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 4dbb490..c318140 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.app.ActivityManagerNative;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -34,6 +35,7 @@
import android.content.res.Resources;
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
+import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Context.USER_SERVICE;
import static android.Manifest.permission.READ_CONTACTS;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -124,7 +126,7 @@
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
- mLockSettingsService.maybeShowEncryptionNotification(UserHandle.ALL);
+ mLockSettingsService.maybeShowEncryptionNotifications();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
// TODO
}
@@ -176,22 +178,48 @@
* If the account is credential-encrypted, show notification requesting the user to unlock
* the device.
*/
- private void maybeShowEncryptionNotification(UserHandle userHandle) {
- if (UserHandle.ALL.equals(userHandle)) {
- final List<UserInfo> users = mUserManager.getUsers();
- for (int i = 0; i < users.size(); i++) {
- UserHandle user = users.get(i).getUserHandle();
- if (!mUserManager.isUserUnlocked(user)) {
- showEncryptionNotification(user);
+ private void maybeShowEncryptionNotifications() {
+ final List<UserInfo> users = mUserManager.getUsers();
+ for (int i = 0; i < users.size(); i++) {
+ UserInfo user = users.get(i);
+ UserHandle userHandle = user.getUserHandle();
+ if (!mUserManager.isUserUnlocked(userHandle)) {
+ if (!user.isManagedProfile()) {
+ showEncryptionNotification(userHandle);
+ } else {
+ UserInfo parent = mUserManager.getProfileParent(user.id);
+ if (parent != null && mUserManager.isUserUnlocked(parent.getUserHandle())) {
+ // Only show notifications for managed profiles once their parent
+ // user is unlocked.
+ showEncryptionNotificationForProfile(userHandle);
+ }
}
}
- } else if (!mUserManager.isUserUnlocked(userHandle)){
- showEncryptionNotification(userHandle);
}
}
+ private void showEncryptionNotificationForProfile(UserHandle user) {
+ Resources r = mContext.getResources();
+ CharSequence title = r.getText(
+ com.android.internal.R.string.user_encrypted_title);
+ CharSequence message = r.getText(
+ com.android.internal.R.string.profile_encrypted_message);
+ CharSequence detail = r.getText(
+ com.android.internal.R.string.profile_encrypted_detail);
+
+ final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
+ final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier());
+ if (unlockIntent == null) {
+ return;
+ }
+ unlockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ showEncryptionNotification(user, title, message, detail, intent);
+ }
+
private void showEncryptionNotification(UserHandle user) {
- if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
Resources r = mContext.getResources();
CharSequence title = r.getText(
com.android.internal.R.string.user_encrypted_title);
@@ -203,6 +231,12 @@
PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
PendingIntent.FLAG_UPDATE_CURRENT);
+ showEncryptionNotification(user, title, message, detail, intent);
+ }
+
+ private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message,
+ CharSequence detail, PendingIntent intent) {
+ if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
Notification notification = new Notification.Builder(mContext)
.setSmallIcon(com.android.internal.R.drawable.ic_user_secure)
.setWhen(0)
@@ -230,8 +264,21 @@
hideEncryptionNotification(new UserHandle(userId));
}
- public void onUnlockUser(int userHandle) {
- hideEncryptionNotification(new UserHandle(userHandle));
+ public void onUnlockUser(int userId) {
+ hideEncryptionNotification(new UserHandle(userId));
+
+ // Now we have unlocked the parent user we should show notifications
+ // about any profiles that exist.
+ List<UserInfo> profiles = mUserManager.getProfiles(userId);
+ for (int i = 0; i < profiles.size(); i++) {
+ UserInfo profile = profiles.get(i);
+ if (profile.isManagedProfile()) {
+ UserHandle userHandle = profile.getUserHandle();
+ if (!mUserManager.isUserUnlocked(userHandle)) {
+ showEncryptionNotificationForProfile(userHandle);
+ }
+ }
+ }
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 95f5734..799d0bd 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2086,7 +2086,7 @@
final int oldUidFirewallRule = uidFirewallRules.get(uid, FIREWALL_RULE_DEFAULT);
if (DBG) {
Slog.d(TAG, "oldRule = " + oldUidFirewallRule
- + ", newRule=" + rule + " for uid=" + uid);
+ + ", newRule=" + rule + " for uid=" + uid + " on chain " + chain);
}
if (oldUidFirewallRule == rule) {
if (DBG) Slog.d(TAG, "!!!!! Skipping change");
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index bef6f0a..1b9d968 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -62,7 +62,7 @@
static final boolean DEBUG_LRU = DEBUG_ALL || false;
static final boolean DEBUG_MU = DEBUG_ALL || false;
static final boolean DEBUG_OOM_ADJ = DEBUG_ALL || false;
- static final boolean DEBUG_PAUSE = DEBUG_ALL || true;
+ static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
static final boolean DEBUG_POWER = DEBUG_ALL || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
static final boolean DEBUG_PROCESS_OBSERVERS = DEBUG_ALL || false;
@@ -85,7 +85,7 @@
static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
- static final boolean DEBUG_VISIBILITY = DEBUG_ALL || true;
+ static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
static final boolean DEBUG_VISIBLE_BEHIND = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false;
static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 75dabc96..1103ea4 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -768,7 +768,6 @@
r.state = ActivityState.RESUMED;
if (DEBUG_STATES) Slog.v(TAG_STATES,
"Moving to RESUMED: " + r + " (starting new instance)");
- r.stopped = false;
mResumedActivity = r;
r.task.touchActiveTime();
mRecentTasks.addLocked(r.task);
@@ -1228,9 +1227,11 @@
* this function updates the rest of our state to match that fact.
*/
private void completeResumeLocked(ActivityRecord next) {
+ next.visible = true;
next.idle = false;
next.results = null;
next.newIntents = null;
+ next.stopped = false;
if (next.isHomeActivity()) {
ProcessRecord app = next.task.mActivities.get(0).app;
@@ -1729,6 +1730,8 @@
// This activity is not currently visible, but is running. Tell it to become visible.
if (r.state == ActivityState.RESUMED || r == starting) {
+ Slog.d(TAG_VISIBILITY, "Not making visible, r=" + r + " state=" + r.state
+ + " starting=" + starting);
return;
}
@@ -2291,7 +2294,6 @@
// From this point on, if something goes wrong there is no way
// to recover the activity.
try {
- next.visible = true;
completeResumeLocked(next);
} catch (Exception e) {
// If any exception gets thrown, toss away this
@@ -2302,8 +2304,6 @@
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
- next.stopped = false;
-
} else {
// Whoops, need to restart this activity!
if (!next.hasBeenLaunched) {
@@ -4283,6 +4283,12 @@
// "restart!".
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is relaunching resumed " + r);
+
+ if (DEBUG_STATES && !r.visible) {
+ Slog.v(TAG_STATES, "Config is relaunching resumed invisible activity " + r
+ + " called by " + Debug.getCallers(4));
+ }
+
relaunchActivityLocked(r, r.configChangeFlags, true, preserveWindow);
} else {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -4433,9 +4439,21 @@
}
if (andResume) {
- r.results = null;
- r.newIntents = null;
+ if (DEBUG_STATES) {
+ Slog.d(TAG_STATES, "Resumed after relaunch " + r);
+ }
r.state = ActivityState.RESUMED;
+ // Relaunch-resume could happen either when the app is already in the front,
+ // or while it's being brought to front. In the latter case, it's marked RESUMED
+ // but not yet visible (or stopped). We need to complete the resume here as the
+ // code in resumeTopActivityInnerLocked to complete the resume might be skipped.
+ if (!r.visible || r.stopped) {
+ mWindowManager.setAppVisibility(r.appToken, true);
+ completeResumeLocked(r);
+ } else {
+ r.results = null;
+ r.newIntents = null;
+ }
} else {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
r.state = ActivityState.PAUSED;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e90b5db..93a36eb 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1046,10 +1046,14 @@
}
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) {
+ return resolveIntent(intent, resolvedType, userId, 0);
+ }
+
+ ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
try {
return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
+ PackageManager.MATCH_DEFAULT_ONLY | flags
+ | ActivityManagerService.STOCK_PM_FLAGS, userId);
} catch (RemoteException e) {
}
return null;
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 1ed749f..9b2bca0 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.server.am;
import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 28be456..cca6fc5 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.server.am;
import static android.app.Activity.RESULT_CANCELED;
@@ -74,7 +90,9 @@
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -84,6 +102,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManager;
import android.service.voice.IVoiceInteractionSession;
import android.util.EventLog;
import android.util.Slog;
@@ -582,6 +601,22 @@
intent = new Intent(intent);
ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
+ if (rInfo == null) {
+ UserInfo userInfo = mSupervisor.getUserInfo(userId);
+ if (userInfo != null && userInfo.isManagedProfile()) {
+ // Special case for managed profiles, if attempting to launch non-cryto aware
+ // app in a locked managed profile from an unlocked parent allow it to resolve
+ // as user will be sent via confirm credentials to unlock the profile.
+ UserManager userManager = UserManager.get(mService.mContext);
+ UserInfo parent = userManager.getProfileParent(userId);
+ if (parent != null
+ && userManager.isUserUnlocked(parent.getUserHandle())
+ && !userManager.isUserUnlocked(userInfo.getUserHandle())) {
+ rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
+ }
+ }
+ }
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index b45430d..16fd909 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1009,6 +1009,7 @@
String label = null;
String iconFilename = null;
int colorPrimary = 0;
+ int colorBackground = 0;
for (--activityNdx; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
if (r.taskDescription != null) {
@@ -1021,9 +1022,13 @@
if (colorPrimary == 0) {
colorPrimary = r.taskDescription.getPrimaryColor();
}
+ if (colorBackground == 0) {
+ colorBackground = r.taskDescription.getBackgroundColor();
+ }
}
}
- lastTaskDescription = new TaskDescription(label, colorPrimary, iconFilename);
+ lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary,
+ colorBackground);
// Update the task affiliation color if we are the parent of the group
if (taskId == mAffiliatedTaskId) {
mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor();
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a355fa4..90ebe18 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -165,15 +165,15 @@
void register(ContentResolver resolver) {
resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
synchronized (mService) {
- updateCurrentUserSetupCompleteLocked();
+ updateUserSetupCompleteLocked(UserHandle.USER_ALL);
}
}
@Override
- public void onChange(boolean selfChange, Uri uri) {
+ public void onChange(boolean selfChange, Uri uri, int userId) {
if (mUserSetupComplete.equals(uri)) {
synchronized (mService) {
- updateCurrentUserSetupCompleteLocked();
+ updateUserSetupCompleteLocked(userId);
}
}
}
@@ -297,6 +297,22 @@
null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
userId);
+ if (getUserInfo(userId).isManagedProfile()) {
+ UserInfo parent = getUserManager().getProfileParent(userId);
+ if (parent != null) {
+ final Intent profileUnlockedIntent = new Intent(
+ Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+ unlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+ unlockedIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ mService.broadcastIntentLocked(null, null, profileUnlockedIntent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID,
+ parent.id);
+ }
+ }
+
final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -655,7 +671,7 @@
final Integer userIdInt = userId;
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
- updateCurrentUserSetupCompleteLocked();
+ updateUserSetupCompleteLocked(userId);
if (foreground) {
mCurrentUserId = userId;
@@ -870,11 +886,16 @@
mUserSwitchObservers.finishBroadcast();
}
- void updateCurrentUserSetupCompleteLocked() {
+ void updateUserSetupCompleteLocked(int userId) {
final ContentResolver cr = mService.mContext.getContentResolver();
- final boolean setupComplete =
- Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, mCurrentUserId) != 0;
- mSetupCompletedUsers.put(mCurrentUserId, setupComplete);
+ for (int i = mStartedUsers.size() - 1; i >= 0; i--) {
+ int startedUser = mStartedUsers.keyAt(i);
+ if (startedUser == userId || userId == UserHandle.USER_ALL) {
+ final boolean setupComplete =
+ Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, startedUser) != 0;
+ mSetupCompletedUsers.put(startedUser, setupComplete);
+ }
+ }
}
boolean isUserSetupCompleteLocked(int userId) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 218e529..0d97434 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -62,6 +62,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Messenger;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -70,41 +71,42 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
-import android.os.Messenger;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.EventLog;
import android.util.Log;
-import android.util.Slog;
import android.util.Pair;
-
+import android.util.Slog;
import android.util.SparseArray;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
import com.android.internal.R;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.accounts.AccountManagerService;
+import com.android.server.backup.AccountSyncSettingsBackupHelper;
import com.android.server.content.SyncStorageEngine.AuthorityInfo;
import com.android.server.content.SyncStorageEngine.EndPoint;
import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Random;
-import java.util.List;
-import java.util.Set;
-import java.util.HashSet;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Map;
-import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
/**
* Implementation details:
@@ -451,10 +453,48 @@
}
}
}
+ ensureDefaultPeriodicSyncsH();
}
});
}
+ private void ensureDefaultPeriodicSyncsH() {
+
+ long defaultPeriod = SyncStorageEngine.DEFAULT_POLL_FREQUENCY_SECONDS;
+ long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(defaultPeriod);
+
+ List<AuthorityInfo> authorities = mSyncStorageEngine.getAllAuthorities();
+ List<SyncOperation> syncs = getAllPendingSyncsFromCache();
+ for (AuthorityInfo authority: authorities) {
+ boolean foundPeriodicSync = false;
+ for (SyncOperation op: syncs) {
+ if (op.isPeriodic && authority.target.matchesSpec(op.target)) {
+ foundPeriodicSync = true;
+ break;
+ }
+ }
+ if (!foundPeriodicSync) {
+ EndPoint target = authority.target;
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType>
+ syncAdapterInfo = mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(
+ target.provider, target.account.type),
+ target.userId);
+ if (syncAdapterInfo == null) {
+ continue;
+ }
+ scheduleSyncOperationH(
+ new SyncOperation(target, syncAdapterInfo.uid,
+ syncAdapterInfo.componentName.getPackageName(),
+ SyncOperation.REASON_PERIODIC, SyncStorageEngine.SOURCE_PERIODIC,
+ new Bundle(), syncAdapterInfo.type.allowParallelSyncs(),
+ true /* periodic */, SyncOperation.NO_JOB_ID, defaultPeriod * 1000L,
+ defaultFlex * 1000L)
+ );
+ }
+ }
+ }
+
private synchronized void verifyJobScheduler() {
if (mJobScheduler != null) {
return;
@@ -510,12 +550,12 @@
mSyncStorageEngine.setPeriodicSyncAddedListener(
new SyncStorageEngine.PeriodicSyncAddedListener() {
- @Override
- public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency,
- long flex) {
- updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
- }
- });
+ @Override
+ public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency,
+ long flex) {
+ updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
+ }
+ });
mSyncStorageEngine.setOnAuthorityRemovedListener(new SyncStorageEngine.OnAuthorityRemovedListener() {
@Override
@@ -750,8 +790,8 @@
* @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state.
*/
public void scheduleSync(Account requestedAccount, int userId, int reason,
- String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
- long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
+ String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
+ long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (extras == null) {
extras = new Bundle();
@@ -941,7 +981,7 @@
* flexMillis will be updated.
*/
public void updateOrAddPeriodicSync(EndPoint target, long pollFrequency, long flex,
- Bundle extras) {
+ Bundle extras) {
UpdatePeriodicSyncMessagePayload payload = new UpdatePeriodicSyncMessagePayload(target,
pollFrequency, flex, extras);
mSyncHandler.obtainMessage(SyncHandler.MESSAGE_UPDATE_PERIODIC_SYNC, payload)
@@ -995,7 +1035,7 @@
}
private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
- SyncResult syncResult) {
+ SyncResult syncResult) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_SYNC_FINISHED");
Message msg = mSyncHandler.obtainMessage();
msg.what = SyncHandler.MESSAGE_SYNC_FINISHED;
@@ -1053,7 +1093,7 @@
public final SyncResult syncResult;
SyncFinishedOrCancelledMessagePayload(ActiveSyncContext syncContext,
- SyncResult syncResult) {
+ SyncResult syncResult) {
this.activeSyncContext = syncContext;
this.syncResult = syncResult;
}
@@ -1066,7 +1106,7 @@
public final Bundle extras;
UpdatePeriodicSyncMessagePayload(EndPoint target, long pollFrequency, long flex,
- Bundle extras) {
+ Bundle extras) {
this.target = target;
this.pollFrequency = pollFrequency;
this.flex = flex;
@@ -1297,7 +1337,7 @@
JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;
JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
- new ComponentName(mContext, SyncJobService.class))
+ new ComponentName(mContext, SyncJobService.class))
.setExtras(syncOperation.toJobInfoExtras())
.setRequiredNetworkType(networkType)
.setPersisted(true)
@@ -1502,7 +1542,7 @@
* for this sync. This is used to attribute the wakelock hold to that application.
*/
public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
- int syncAdapterUid) {
+ int syncAdapterUid) {
super();
mSyncAdapterUid = syncAdapterUid;
mSyncOperation = syncOperation;
@@ -1731,7 +1771,7 @@
new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
@Override
public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
- RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
+ RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
return lhs.type.authority.compareTo(rhs.type.authority);
}
});
@@ -2572,6 +2612,7 @@
}
private void updateRunningAccountsH(EndPoint syncTargets) {
+ AccountAndUser[] oldAccounts = mRunningAccounts;
mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "Accounts list: ");
@@ -2595,6 +2636,17 @@
}
}
+ // On account add, check if there are any settings to be restored.
+ for (AccountAndUser aau : mRunningAccounts) {
+ if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Account " + aau.account + " added, checking sync restore data");
+ }
+ AccountSyncSettingsBackupHelper.accountAdded(mContext);
+ break;
+ }
+ }
+
List<SyncOperation> ops = getAllPendingSyncsFromCache();
for (SyncOperation op: ops) {
if (!containsAccountAndUser(accounts, op.target.account, op.target.userId)) {
@@ -2618,7 +2670,7 @@
* @param flexMillis new flex time in milliseconds.
*/
private void maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis,
- long flexMillis) {
+ long flexMillis) {
if (!(pollFrequencyMillis == syncOperation.periodMillis
&& flexMillis == syncOperation.flexMillis)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -2633,7 +2685,7 @@
}
private void updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex,
- Bundle extras) {
+ Bundle extras) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
verifyJobScheduler(); // Will fill in mScheduledSyncs cache if it is not already filled.
final long pollFrequencyMillis = pollFrequency * 1000L;
@@ -2821,7 +2873,7 @@
}
private void runBoundToAdapterH(final ActiveSyncContext activeSyncContext,
- IBinder syncAdapter) {
+ IBinder syncAdapter) {
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
try {
activeSyncContext.mIsLinkedToDeath = true;
@@ -2889,7 +2941,7 @@
}
private void runSyncFinishedOrCanceledH(SyncResult syncResult,
- ActiveSyncContext activeSyncContext) {
+ ActiveSyncContext activeSyncContext) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
@@ -3023,7 +3075,7 @@
}
private void installHandleTooManyDeletesNotification(Account account, String authority,
- long numDeletes, int userId) {
+ long numDeletes, int userId) {
if (mNotificationMgr == null) return;
final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
@@ -3099,7 +3151,7 @@
}
public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
- int upstreamActivity, int downstreamActivity, long elapsedTime) {
+ int upstreamActivity, int downstreamActivity, long elapsedTime) {
EventLog.writeEvent(2720,
syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
@@ -3263,4 +3315,4 @@
return mContext;
}
}
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 965054f..cefaa8d 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -80,7 +80,7 @@
private static final String XML_TAG_LISTEN_FOR_TICKLES = "listenForTickles";
/** Default time for a periodic sync. */
- private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
+ static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
/** Percentage of period that is flex by default, if no flexMillis is set. */
private static final double DEFAULT_FLEX_PERCENT_SYNC = 0.04;
@@ -857,6 +857,16 @@
}
}
+ List<AuthorityInfo> getAllAuthorities() {
+ List<AuthorityInfo> authorities = new ArrayList<AuthorityInfo>();
+ synchronized (mAuthorities) {
+ for (int i = 0; i < mAuthorities.size(); i++) {
+ authorities.add(mAuthorities.valueAt(i));
+ }
+ }
+ return authorities;
+ }
+
/**
* Returns true if there is currently a sync operation being actively processed for the given
* target.
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index e5e86ac..40283728 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -330,7 +330,7 @@
private void cancelJobImpl(JobStatus cancelled) {
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
- stopTrackingJob(cancelled);
+ stopTrackingJob(cancelled, true /* writeBack */);
synchronized (mJobs) {
// Remove from pending queue.
mPendingJobs.remove(cancelled);
@@ -509,12 +509,12 @@
* Called when we want to remove a JobStatus object that we've finished executing. Returns the
* object removed.
*/
- private boolean stopTrackingJob(JobStatus jobStatus) {
+ private boolean stopTrackingJob(JobStatus jobStatus, boolean writeBack) {
boolean removed;
boolean rocking;
synchronized (mJobs) {
// Remove from store as well as controllers.
- removed = mJobs.remove(jobStatus);
+ removed = mJobs.remove(jobStatus, writeBack);
rocking = mReadyToRock;
}
if (removed && rocking) {
@@ -645,7 +645,9 @@
if (DEBUG) {
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
}
- if (!stopTrackingJob(jobStatus)) {
+ // Do not write back immediately if this is a periodic job. The job may get lost if system
+ // shuts down before it is added back.
+ if (!stopTrackingJob(jobStatus, !jobStatus.getJob().isPeriodic())) {
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
}
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index f796164..f6a6778 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -159,9 +159,10 @@
/**
* Remove the provided job. Will also delete the job if it was persisted.
+ * @param writeBack If true, the job will be deleted (if it was persisted) immediately.
* @return Whether or not the job existed to be removed.
*/
- public boolean remove(JobStatus jobStatus) {
+ public boolean remove(JobStatus jobStatus, boolean writeBack) {
boolean removed = mJobSet.remove(jobStatus);
if (!removed) {
if (DEBUG) {
@@ -169,7 +170,7 @@
}
return false;
}
- if (jobStatus.isPersisted()) {
+ if (writeBack && jobStatus.isPersisted()) {
maybeWriteStatusToDiskAsync();
}
return removed;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index ffc52b3..c1eb844 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1557,15 +1557,13 @@
* called from native code to update SV info
*/
private void reportSvStatus() {
- int svCount = native_read_sv_status(mPrnWithFlags, mSnrs, mSvElevations, mSvAzimuths,
- mConstellationTypes);
+ int svCount = native_read_sv_status(mSvidWithFlags, mSnrs, mSvElevations, mSvAzimuths);
mListenerHelper.onSvStatusChanged(
svCount,
- mPrnWithFlags,
+ mSvidWithFlags,
mSnrs,
mSvElevations,
- mSvAzimuths,
- mConstellationTypes);
+ mSvAzimuths);
if (VERBOSE) {
Log.v(TAG, "SV count: " + svCount);
@@ -1573,19 +1571,19 @@
// Calculate number of sets used in fix.
int usedInFixCount = 0;
for (int i = 0; i < svCount; i++) {
- if ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
+ if ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
++usedInFixCount;
}
if (VERBOSE) {
- Log.v(TAG, "prn: " + (mPrnWithFlags[i] >> GnssStatus.PRN_SHIFT_WIDTH) +
+ Log.v(TAG, "svid: " + (mSvidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH) +
" snr: " + mSnrs[i]/10 +
" elev: " + mSvElevations[i] +
" azimuth: " + mSvAzimuths[i] +
- ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
+ ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
? " " : " E") +
- ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
+ ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
? " " : " A") +
- ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
+ ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
? "" : "U"));
}
}
@@ -2398,14 +2396,13 @@
}
// for GPS SV statistics
- private static final int MAX_SVS = 512;
+ private static final int MAX_SVS = 64;
// preallocated arrays, to avoid memory allocation in reportStatus()
- private int mPrnWithFlags[] = new int[MAX_SVS];
+ private int mSvidWithFlags[] = new int[MAX_SVS];
private float mSnrs[] = new float[MAX_SVS];
private float mSvElevations[] = new float[MAX_SVS];
private float mSvAzimuths[] = new float[MAX_SVS];
- private int mConstellationTypes[] = new int[MAX_SVS];
private int mSvCount;
// preallocated to avoid memory allocation in reportNmea()
private byte[] mNmeaBuffer = new byte[120];
@@ -2426,7 +2423,7 @@
// returns number of SVs
// mask[0] is ephemeris mask and mask[1] is almanac mask
private native int native_read_sv_status(int[] prnWithFlags, float[] snrs, float[] elevations,
- float[] azimuths, int[] constellationTypes);
+ float[] azimuths);
private native int native_read_nmea(byte[] buffer, int bufferSize);
private native void native_inject_location(double latitude, double longitude, float accuracy);
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index 9840c61..0b3111c 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -77,8 +77,7 @@
final int[] prnWithFlags,
final float[] snrs,
final float[] elevations,
- final float[] azimuths,
- final int[] constellationTypes) {
+ final float[] azimuths) {
Operation operation = new Operation() {
@Override
public void execute(IGnssStatusListener listener) throws RemoteException {
@@ -87,8 +86,7 @@
prnWithFlags,
snrs,
elevations,
- azimuths,
- constellationTypes);
+ azimuths);
}
};
foreach(operation);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b2e6adf..426ce41 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -339,6 +339,8 @@
private final AppOpsManager mAppOps;
private final MyPackageMonitor mPackageMonitor;
+ private final IPackageManager mIPm;
+
// TODO: keep whitelist of system-critical services that should never have
// rules enforced, such as system, phone, and radio UIDs.
@@ -369,6 +371,7 @@
Context.DEVICE_IDLE_CONTROLLER));
mTime = checkNotNull(time, "missing TrustedTime");
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mIPm = AppGlobals.getPackageManager();
HandlerThread thread = new HandlerThread(TAG);
thread.start();
@@ -1864,31 +1867,58 @@
@Override
public void addRestrictBackgroundWhitelistedUid(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist");
+ if (!isUidValidForRules(uid)) return;
+ final boolean changed;
synchronized (mRulesLock) {
+ final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
+ if (oldStatus) {
+ if (LOGD) Slog.d(TAG, "uid " + uid + " is already whitelisted");
+ return;
+ }
+ Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist");
mRestrictBackgroundWhitelistUids.append(uid, true);
- updateRulesForGlobalChangeLocked(true);
+ changed = mRestrictBackground && !oldStatus;
+ if (changed && hasInternetPermissions(uid)) {
+ setUidNetworkRules(uid, false);
+ }
writePolicyLocked();
}
- mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0).sendToTarget();
+ if (changed) {
+ mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
+ .sendToTarget();
+ }
}
@Override
public void removeRestrictBackgroundWhitelistedUid(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- Slog.i(TAG, "removing uid " + uid + " from restrict background whitelist");
+ if (!isUidValidForRules(uid)) return;
+ final boolean changed;
synchronized (mRulesLock) {
- removeRestrictBackgroundWhitelistedUidLocked(uid, true);
+ changed = removeRestrictBackgroundWhitelistedUidLocked(uid, true);
}
- mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0).sendToTarget();
+ if (changed) {
+ mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
+ .sendToTarget();
+ }
}
- private void removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean updateNow) {
+ private boolean removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean updateNow) {
+ final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
+ if (!oldStatus) {
+ if (LOGD) Slog.d(TAG, "uid " + uid + " was not whitelisted before");
+ return false;
+ }
+ Slog.i(TAG, "removing uid " + uid + " from restrict background whitelist");
+ final boolean changed = mRestrictBackground && oldStatus;
mRestrictBackgroundWhitelistUids.delete(uid);
if (updateNow) {
- updateRulesForGlobalChangeLocked(true);
+ if (changed && hasInternetPermissions(uid)) {
+ setUidNetworkRules(uid, true);
+ }
writePolicyLocked();
}
+ return changed;
}
@Override
@@ -2298,13 +2328,19 @@
uidRules.clear();
// Fully update the app idle firewall chain.
+ final IPackageManager ipm = AppGlobals.getPackageManager();
final List<UserInfo> users = mUserManager.getUsers();
for (int ui = users.size() - 1; ui >= 0; ui--) {
UserInfo user = users.get(ui);
int[] idleUids = mUsageStats.getIdleUidsForUser(user.id);
for (int uid : idleUids) {
if (!mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid), false)) {
- uidRules.put(uid, FIREWALL_RULE_DENY);
+ // quick check: if this uid doesn't have INTERNET permission, it
+ // doesn't have network access anyway, so it is a waste to mess
+ // with it here.
+ if (hasInternetPermissions(uid)) {
+ uidRules.put(uid, FIREWALL_RULE_DENY);
+ }
}
}
}
@@ -2408,22 +2444,27 @@
}
/**
+ * Checks if an uid has INTERNET permissions.
+ * <p>
+ * Useful for the cases where the lack of network access can simplify the rules.
+ */
+ private boolean hasInternetPermissions(int uid) {
+ try {
+ if (mIPm.checkUidPermission(Manifest.permission.INTERNET, uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ } catch (RemoteException e) {
+ }
+ return true;
+ }
+
+ /**
* Applies network rules to bandwidth and firewall controllers based on uid policy.
* @param uid The uid for which to apply the latest policy
*/
void updateRulesForUidLocked(int uid) {
- if (!isUidValidForRules(uid)) return;
-
- // quick check: if this uid doesn't have INTERNET permission, it doesn't have
- // network access anyway, so it is a waste to mess with it here.
- final IPackageManager ipm = AppGlobals.getPackageManager();
- try {
- if (ipm.checkUidPermission(Manifest.permission.INTERNET, uid)
- != PackageManager.PERMISSION_GRANTED) {
- return;
- }
- } catch (RemoteException e) {
- }
+ if (!isUidValidForRules(uid) || !hasInternetPermissions(uid)) return;
final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
final boolean uidForeground = isUidForegroundLocked(uid);
@@ -2598,7 +2639,6 @@
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
-
return true;
}
case MSG_ADVISE_PERSIST_THRESHOLD: {
@@ -2831,13 +2871,5 @@
removeRestrictBackgroundWhitelistedUidLocked(uid, true);
}
}
-
- @Override
- public void onPackageRemovedAllUsers(String packageName, int uid) {
- if (LOGV) Slog.v(TAG, "onPackageRemovedAllUsers: " + packageName + " ->" + uid);
- synchronized (mRulesLock) {
- removeRestrictBackgroundWhitelistedUidLocked(uid, true);
- }
- }
}
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index ce18818..9820a12 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -234,9 +234,13 @@
final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
r.info = info;
r.condition = c;
- if (mCallback != null) {
- mCallback.onConditionChanged(c.id, c);
- }
+ }
+ }
+ final int N = conditions.length;
+ for (int i = 0; i < N; i++) {
+ final Condition c = conditions[i];
+ if (mCallback != null) {
+ mCallback.onConditionChanged(c.id, c);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 36ddbcf..0519cf2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1350,9 +1350,9 @@
}
@Override
- public boolean doesAppUseTopics(String pkg, int uid) {
+ public boolean doesUserUseTopics(String pkg, int uid) {
enforceSystemOrSystemUI("Caller not system or systemui");
- return mRankingHelper.doesAppUseTopics(pkg, uid);
+ return mRankingHelper.doesUserUseTopics(pkg, uid);
}
/**
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 17bb907..9773474 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -36,7 +36,7 @@
int getImportance(String packageName, int uid, Notification.Topic topic);
- boolean doesAppUseTopics(String packageName, int uid);
+ boolean doesUserUseTopics(String packageName, int uid);
boolean hasBannedTopics(String packageName, int uid);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 6554bf9..91eab10 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -521,15 +521,14 @@
}
@Override
- public boolean doesAppUseTopics(String pkgName, int uid) {
+ public boolean doesUserUseTopics(String pkgName, int uid) {
final Record r = getOrCreateRecord(pkgName, uid);
- int numTopics = r.topics.size();
- if (numTopics == 0
- || (numTopics == 1 && r.topics.containsKey(Notification.TOPIC_DEFAULT))) {
- return false;
- } else {
- return true;
+ for (Topic topic : r.topics.values()) {
+ if (topic.importance != Ranking.IMPORTANCE_UNSPECIFIED
+ && r.importance != topic.importance)
+ return true;
}
+ return false;
}
private Topic getOrCreateTopic(Record r, Notification.Topic topic) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index d82bb3d..c6613f5 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -201,8 +201,7 @@
long ident = Binder.clearCallingIdentity();
try {
List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return new ParceledListSlice<>(apps);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -220,7 +219,7 @@
long ident = Binder.clearCallingIdentity();
try {
ResolveInfo app = mPm.resolveActivityAsUser(intent,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return app;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -239,7 +238,7 @@
try {
IPackageManager pm = AppGlobals.getPackageManager();
PackageInfo info = pm.getPackageInfo(packageName,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return info != null && info.applicationInfo.enabled;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -277,7 +276,7 @@
try {
IPackageManager pm = AppGlobals.getPackageManager();
ActivityInfo info = pm.getActivityInfo(component,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return info != null;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -303,7 +302,7 @@
try {
IPackageManager pm = AppGlobals.getPackageManager();
ActivityInfo info = pm.getActivityInfo(component,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
if (!info.exported) {
throw new SecurityException("Cannot launch non-exported components "
+ component);
@@ -313,7 +312,7 @@
// as calling startActivityAsUser ignores the category and just
// resolves based on the component if present.
List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
final int size = apps.size();
for (int i = 0; i < size; ++i) {
ActivityInfo activityInfo = apps.get(i).activityInfo;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8a94ce5..92343d6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13256,16 +13256,19 @@
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
PackageSetting ps = mSettings.mPackages.get(pkgName);
+ final int userId = user.getIdentifier();
if (ps != null) {
if (isSystemApp(newPackage)) {
- // NB: implicit assumption that system package upgrades apply to all users
if (DEBUG_INSTALL) {
Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
}
+ // Enable system package for requested users
if (res.origUsers != null) {
- for (int userHandle : res.origUsers) {
- ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
- userHandle, installerPackageName);
+ for (int origUserId : res.origUsers) {
+ if (userId == UserHandle.USER_ALL || userId == origUserId) {
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
+ origUserId, installerPackageName);
+ }
}
}
// Also convey the prior install/uninstall state
@@ -13283,7 +13286,6 @@
}
// It's implied that when a user requests installation, they want the app to be
// installed and enabled.
- int userId = user.getIdentifier();
if (userId != UserHandle.USER_ALL) {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 22d0994..76d6b28 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1934,13 +1934,18 @@
if (user == null) {
return null;
}
- setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
- // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
- // the putIntForUser() will fail.
- android.provider.Settings.Secure.putIntForUser(mContext.getContentResolver(),
- android.provider.Settings.Secure.LOCATION_MODE,
- android.provider.Settings.Secure.LOCATION_MODE_OFF, user.id);
- setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user.id);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
+ // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
+ // the putIntForUser() will fail.
+ android.provider.Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ android.provider.Settings.Secure.LOCATION_MODE,
+ android.provider.Settings.Secure.LOCATION_MODE_OFF, user.id);
+ setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user.id);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
return user;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c046ba6..3a70e67 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -18,6 +18,8 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -32,6 +34,7 @@
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityManagerNative;
@@ -135,6 +138,7 @@
import com.android.server.LocalServices;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
+import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.File;
import java.io.FileReader;
@@ -280,6 +284,7 @@
DreamManagerInternal mDreamManagerInternal;
PowerManagerInternal mPowerManagerInternal;
IStatusBarService mStatusBarService;
+ StatusBarManagerInternal mStatusBarManagerInternal;
boolean mPreloadedRecentApps;
final Object mServiceAquireLock = new Object();
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -488,6 +493,13 @@
int mResettingSystemUiFlags = 0;
// Bits that we are currently always keeping cleared.
int mForceClearedSystemUiFlags = 0;
+ int mLastFullscreenStackSysUiFlags;
+ int mLastDockedStackSysUiFlags;
+ final Rect mNonDockedStackBounds = new Rect();
+ final Rect mDockedStackBounds = new Rect();
+ final Rect mLastNonDockedStackBounds = new Rect();
+ final Rect mLastDockedStackBounds = new Rect();
+
// What we last reported to system UI about whether the compatibility
// menu needs to be displayed.
boolean mLastFocusNeedsMenu = false;
@@ -508,6 +520,8 @@
WindowState mTopFullscreenOpaqueWindowState;
WindowState mTopFullscreenOpaqueOrDimmingWindowState;
+ WindowState mTopDockedOpaqueWindowState;
+ WindowState mTopDockedOpaqueOrDimmingWindowState;
HashSet<IApplicationToken> mAppsToBeHidden = new HashSet<IApplicationToken>();
HashSet<IApplicationToken> mAppsThatDismissKeyguard = new HashSet<IApplicationToken>();
boolean mTopIsFullscreen;
@@ -844,6 +858,16 @@
}
}
+ StatusBarManagerInternal getStatusBarManagerInternal() {
+ synchronized (mServiceAquireLock) {
+ if (mStatusBarManagerInternal == null) {
+ mStatusBarManagerInternal =
+ LocalServices.getService(StatusBarManagerInternal.class);
+ }
+ return mStatusBarManagerInternal;
+ }
+ }
+
/*
* We always let the sensor be switched on by default except when
* the user has explicitly disabled sensor based rotation or when the
@@ -4373,6 +4397,23 @@
+ mUnrestrictedScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom = mUnrestrictedScreenTop
+ mUnrestrictedScreenHeight;
+ } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
+ pf.left = df.left = of.left = mRestrictedScreenLeft;
+ pf.top = df.top = of.top = mRestrictedScreenTop;
+ pf.right = df.right = of.right = mRestrictedScreenLeft + mRestrictedScreenWidth;
+ pf.bottom = df.bottom = of.bottom = mRestrictedScreenTop
+ + mRestrictedScreenHeight;
+ if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+ cf.left = mDockLeft;
+ cf.top = mDockTop;
+ cf.right = mDockRight;
+ cf.bottom = mDockBottom;
+ } else {
+ cf.left = mContentLeft;
+ cf.top = mContentTop;
+ cf.right = mContentRight;
+ cf.bottom = mContentBottom;
+ }
} else {
pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft;
pf.top = df.top = of.top = cf.top = mRestrictedScreenTop;
@@ -4552,6 +4593,8 @@
public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
mTopFullscreenOpaqueWindowState = null;
mTopFullscreenOpaqueOrDimmingWindowState = null;
+ mTopDockedOpaqueWindowState = null;
+ mTopDockedOpaqueOrDimmingWindowState = null;
mAppsToBeHidden.clear();
mAppsThatDismissKeyguard.clear();
mForceStatusBar = false;
@@ -4597,7 +4640,7 @@
&& attrs.type < FIRST_SYSTEM_WINDOW;
final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0;
final boolean dismissKeyguard = (fl & FLAG_DISMISS_KEYGUARD) != 0;
-
+ final int stackId = win.getStackId();
if (mTopFullscreenOpaqueWindowState == null &&
win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()) {
if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
@@ -4646,9 +4689,7 @@
} else {
mAppsToBeHidden.add(appToken);
}
- if (attrs.x == 0 && attrs.y == 0
- && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
- && attrs.height == WindowManager.LayoutParams.MATCH_PARENT) {
+ if (isFullscreen(attrs) && StackId.normallyFullscreenWindows(stackId)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
mTopFullscreenOpaqueWindowState = win;
if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
@@ -4692,11 +4733,37 @@
mWinShowWhenLocked = win;
}
}
- if (mTopFullscreenOpaqueOrDimmingWindowState == null
- && win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()
- && win.isDimming()) {
+
+ // Keep track of the window if it's dimming but not necessarily fullscreen.
+ final boolean reallyVisible = win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw();
+ if (mTopFullscreenOpaqueOrDimmingWindowState == null && reallyVisible
+ && win.isDimming() && StackId.normallyFullscreenWindows(stackId)) {
mTopFullscreenOpaqueOrDimmingWindowState = win;
}
+
+ // We need to keep track of the top "fullscreen" opaque window for the docked stack
+ // separately, because both the "real fullscreen" opaque window and the one for the docked
+ // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+ if (mTopDockedOpaqueWindowState == null && reallyVisible && appWindow && attached == null
+ && isFullscreen(attrs) && stackId == DOCKED_STACK_ID) {
+ mTopDockedOpaqueWindowState = win;
+ if (mTopDockedOpaqueOrDimmingWindowState == null) {
+ mTopDockedOpaqueOrDimmingWindowState = win;
+ }
+ }
+
+ // Also keep track of any windows that are dimming but not necessarily fullscreen in the
+ // docked stack.
+ if (mTopDockedOpaqueOrDimmingWindowState == null && reallyVisible && win.isDimming()
+ && stackId == DOCKED_STACK_ID) {
+ mTopDockedOpaqueOrDimmingWindowState = win;
+ }
+ }
+
+ private boolean isFullscreen(WindowManager.LayoutParams attrs) {
+ return attrs.x == 0 && attrs.y == 0
+ && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
+ && attrs.height == WindowManager.LayoutParams.MATCH_PARENT;
}
/** {@inheritDoc} */
@@ -6838,42 +6905,52 @@
tmpVisibility |= StatusBarManager.DISABLE_RECENT;
}
- tmpVisibility = updateLightStatusBarLw(tmpVisibility);
+ final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
+ mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
+ final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
+ mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
+ mWindowManagerFuncs.getStackBounds(HOME_STACK_ID, mNonDockedStackBounds);
+ mWindowManagerFuncs.getStackBounds(DOCKED_STACK_ID, mDockedStackBounds);
final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int diff = visibility ^ mLastSystemUiFlags;
+ final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
+ final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
- if (diff == 0 && mLastFocusNeedsMenu == needsMenu
- && mFocusedApp == win.getAppToken()) {
+ if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
+ && mFocusedApp == win.getAppToken()
+ && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
+ && mLastDockedStackBounds.equals(mDockedStackBounds)) {
return 0;
}
mLastSystemUiFlags = visibility;
+ mLastFullscreenStackSysUiFlags = fullscreenVisibility;
+ mLastDockedStackSysUiFlags = dockedVisibility;
mLastFocusNeedsMenu = needsMenu;
mFocusedApp = win.getAppToken();
+ final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
+ final Rect dockedStackBounds = new Rect(mDockedStackBounds);
mHandler.post(new Runnable() {
@Override
public void run() {
- try {
- IStatusBarService statusbar = getStatusBarService();
- if (statusbar != null) {
- statusbar.setSystemUiVisibility(visibility, 0xffffffff, win.toString());
- statusbar.topAppWindowChanged(needsMenu);
- }
- } catch (RemoteException e) {
- // re-acquire status bar service next time it is needed.
- mStatusBarService = null;
+ StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+ if (statusbar != null) {
+ statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,
+ dockedVisibility, 0xffffffff, fullscreenStackBounds,
+ dockedStackBounds, win.toString());
+ statusbar.topAppWindowChanged(needsMenu);
}
}
});
return diff;
}
- private int updateLightStatusBarLw(int vis) {
+ private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
WindowState statusColorWin = isStatusBarKeyguard() && !mHideLockScreen
? mStatusBar
- : mTopFullscreenOpaqueOrDimmingWindowState;
+ : opaqueOrDimming;
if (statusColorWin != null) {
- if (statusColorWin == mTopFullscreenOpaqueWindowState) {
+ if (statusColorWin == opaque) {
// If the top fullscreen-or-dimming window is also the top fullscreen, respect
// its light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 25d646d..cbbcdae 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -16,6 +16,7 @@
package com.android.server.statusbar;
+import android.graphics.Rect;
import android.os.Bundle;
import com.android.server.notification.NotificationDelegate;
@@ -29,4 +30,7 @@
void showAssistDisclosure();
void startAssist(Bundle args);
void onCameraLaunchGestureDetected(int source);
+ void topAppWindowChanged(boolean menuVisible);
+ void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+ Rect fullscreenBounds, Rect dockedBounds, String cause);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 90340d5..6eab8d4 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -20,6 +20,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -70,6 +71,10 @@
private Object mLock = new Object();
// encompasses lights-out mode and other flags defined on View
private int mSystemUiVisibility = 0;
+ private int mFullscreenStackSysUiVisibility;
+ private int mDockedStackSysUiVisibility;
+ private final Rect mFullscreenStackBounds = new Rect();
+ private final Rect mDockedStackBounds = new Rect();
private boolean mMenuVisible = false;
private int mImeWindowVis = 0;
private int mImeBackDisposition;
@@ -186,6 +191,19 @@
}
}
}
+
+ @Override
+ public void topAppWindowChanged(boolean menuVisible) {
+ StatusBarManagerService.this.topAppWindowChanged(menuVisible);
+ }
+
+ @Override
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask,
+ Rect fullscreenBounds, Rect dockedBounds, String cause) {
+ StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
+ dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
+ }
};
// ================================================================================
@@ -390,8 +408,7 @@
* response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
* to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
*/
- @Override
- public void topAppWindowChanged(final boolean menuVisible) {
+ private void topAppWindowChanged(final boolean menuVisible) {
enforceStatusBar();
if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
@@ -399,15 +416,15 @@
synchronized(mLock) {
mMenuVisible = menuVisible;
mHandler.post(new Runnable() {
- public void run() {
- if (mBar != null) {
- try {
- mBar.topAppWindowChanged(menuVisible);
- } catch (RemoteException ex) {
- }
+ public void run() {
+ if (mBar != null) {
+ try {
+ mBar.topAppWindowChanged(menuVisible);
+ } catch (RemoteException ex) {
}
}
- });
+ }
+ });
}
}
@@ -443,13 +460,19 @@
@Override
public void setSystemUiVisibility(int vis, int mask, String cause) {
+ setSystemUiVisibility(vis, 0, 0, mask, mFullscreenStackBounds, mDockedStackBounds, cause);
+ }
+
+ private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+ Rect fullscreenBounds, Rect dockedBounds, String cause) {
// also allows calls from window manager which is in this process.
enforceStatusBarService();
if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
synchronized (mLock) {
- updateUiVisibilityLocked(vis, mask);
+ updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
+ fullscreenBounds, dockedBounds);
disableLocked(
mCurrentUserId,
vis & StatusBarManager.DISABLE_MASK,
@@ -458,14 +481,25 @@
}
}
- private void updateUiVisibilityLocked(final int vis, final int mask) {
- if (mSystemUiVisibility != vis) {
+ private void updateUiVisibilityLocked(final int vis,
+ final int fullscreenStackVis, final int dockedStackVis, final int mask,
+ final Rect fullscreenBounds, final Rect dockedBounds) {
+ if (mSystemUiVisibility != vis
+ || mFullscreenStackSysUiVisibility != fullscreenStackVis
+ || mDockedStackSysUiVisibility != dockedStackVis
+ || !mFullscreenStackBounds.equals(fullscreenBounds)
+ || !mDockedStackBounds.equals(dockedBounds)) {
mSystemUiVisibility = vis;
+ mFullscreenStackSysUiVisibility = fullscreenStackVis;
+ mDockedStackSysUiVisibility = dockedStackVis;
+ mFullscreenStackBounds.set(fullscreenBounds);
+ mDockedStackBounds.set(dockedBounds);
mHandler.post(new Runnable() {
public void run() {
if (mBar != null) {
try {
- mBar.setSystemUiVisibility(vis, mask);
+ mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
+ mask, fullscreenBounds, dockedBounds);
} catch (RemoteException ex) {
}
}
@@ -617,7 +651,8 @@
// ================================================================================
@Override
public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
- List<StatusBarIcon> iconList, int switches[], List<IBinder> binders) {
+ List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
+ Rect fullscreenStackBounds, Rect dockedStackBounds) {
enforceStatusBarService();
Slog.i(TAG, "registerStatusBar bar=" + bar);
@@ -636,7 +671,11 @@
switches[4] = mImeBackDisposition;
switches[5] = mShowImeSwitcher ? 1 : 0;
switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
+ switches[7] = mFullscreenStackSysUiVisibility;
+ switches[8] = mDockedStackSysUiVisibility;
binders.add(mImeToken);
+ fullscreenStackBounds.set(mFullscreenStackBounds);
+ dockedStackBounds.set(mDockedStackBounds);
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 3e8269a..f3b120f 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -31,7 +31,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
-import android.provider.Settings.Secure;
+import android.provider.Settings.Global;
import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.webkit.IWebViewUpdateService;
@@ -91,7 +91,7 @@
// change provider when the new version of the package is being installed).
if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
&& intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
- synchronized(this) {
+ synchronized(WebViewUpdateService.this) {
if (mCurrentWebViewPackage == null) return;
String webViewPackage = "package:" + mCurrentWebViewPackage.packageName;
@@ -268,13 +268,13 @@
}
private static String getUserChosenWebViewProvider() {
- return Settings.Secure.getString(AppGlobals.getInitialApplication().getContentResolver(),
- Settings.Secure.WEBVIEW_PROVIDER);
+ return Settings.Global.getString(AppGlobals.getInitialApplication().getContentResolver(),
+ Settings.Global.WEBVIEW_PROVIDER);
}
private void updateUserSetting(String newProviderName) {
- Settings.Secure.putString(getContext().getContentResolver(),
- Settings.Secure.WEBVIEW_PROVIDER,
+ Settings.Global.putString(getContext().getContentResolver(),
+ Settings.Global.WEBVIEW_PROVIDER,
newProviderName == null ? "" : newProviderName);
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index d1c0881..0c04d4d 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -24,6 +24,7 @@
import android.content.ClipData;
import android.content.ClipDescription;
+import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
@@ -33,6 +34,10 @@
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.IUserManager;
import android.util.Slog;
import android.view.Display;
import android.view.DragEvent;
@@ -73,6 +78,8 @@
IBinder mLocalWin;
int mPid;
int mUid;
+ int mSourceUserId;
+ boolean mCrossProfileCopyAllowed;
ClipData mData;
ClipDescription mDataDescription;
int mTouchSource;
@@ -221,6 +228,18 @@
mNotifiedWindows.clear();
mDragInProgress = true;
+ mSourceUserId = UserHandle.getUserId(mUid);
+
+ final IUserManager userManager =
+ (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
+ try {
+ mCrossProfileCopyAllowed = !userManager.getUserRestrictions(mSourceUserId).getBoolean(
+ UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Remote Exception calling UserManager: " + e);
+ mCrossProfileCopyAllowed = false;
+ }
+
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
}
@@ -234,7 +253,7 @@
}
}
- /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the
+ /* helper - send a ACTION_DRAG_STARTED event, if the
* designated window is potentially a drop recipient. There are race situations
* around DRAG_ENDED broadcast, so we make sure that once we've declared that
* the drag has ended, we never send out another DRAG_STARTED for this drag action.
@@ -244,19 +263,7 @@
*/
private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
ClipDescription desc) {
- // Don't actually send the event if the drag is supposed to be pinned
- // to the originating window but 'newWin' is not that window.
- if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
- final IBinder winBinder = newWin.mClient.asBinder();
- if (winBinder != mLocalWin) {
- if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "Not dispatching local DRAG_STARTED to " + newWin);
- }
- return;
- }
- }
-
- if (mDragInProgress && newWin.isPotentialDragTarget()) {
+ if (mDragInProgress && isValidDropTarget(newWin)) {
DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
touchX, touchY, null, desc, null, null, false);
try {
@@ -274,17 +281,33 @@
}
}
- /* helper - construct and send a DRAG_STARTED event only if the window has not
+ private boolean isValidDropTarget(WindowState targetWin) {
+ if (targetWin == null) {
+ return false;
+ }
+ if (!targetWin.isPotentialDragTarget()) {
+ return false;
+ }
+ if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
+ // Drag is limited to the current window.
+ if (mLocalWin != targetWin.mClient.asBinder()) {
+ return false;
+ }
+ }
+
+ return mCrossProfileCopyAllowed ||
+ mSourceUserId == UserHandle.getUserId(targetWin.getOwningUid());
+ }
+
+ /* helper - send a ACTION_DRAG_STARTED event only if the window has not
* previously been notified, i.e. it became visible after the drag operation
* was begun. This is a rare case.
*/
void sendDragStartedIfNeededLw(WindowState newWin) {
if (mDragInProgress) {
// If we have sent the drag-started, we needn't do so again
- for (WindowState ws : mNotifiedWindows) {
- if (ws == newWin) {
- return;
- }
+ if (isWindowNotified(newWin)) {
+ return;
}
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "need to send DRAG_STARTED to new window " + newWin);
@@ -293,6 +316,15 @@
}
}
+ private boolean isWindowNotified(WindowState newWin) {
+ for (WindowState ws : mNotifiedWindows) {
+ if (ws == newWin) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void broadcastDragEndedLw() {
final int myPid = Process.myPid();
@@ -389,14 +421,13 @@
if (DEBUG_DRAG) Slog.d(TAG_WM, "No touched win at x=" + x + " y=" + y);
return;
}
- if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
- final IBinder touchedBinder = touchedWin.mClient.asBinder();
- if (touchedBinder != mLocalWin) {
- // This drag is pinned only to the originating window, but the drag
- // point is outside that window. Pretend it's over empty space.
- touchedWin = null;
- }
+
+ if (!isWindowNotified(touchedWin)) {
+ // The drag point is over a window which was not notified about a drag start.
+ // Pretend it's over empty space.
+ touchedWin = null;
}
+
try {
final int myPid = Process.myPid();
@@ -445,7 +476,7 @@
mCurrentX = x;
mCurrentY = y;
- if (touchedWin == null) {
+ if (!isWindowNotified(touchedWin)) {
// "drop" outside a valid window -- no recipient to apply a
// timeout to, and we can send the drag-ended message immediately.
mDragResult = false;
@@ -455,6 +486,9 @@
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "sending DROP to " + touchedWin);
}
+ if (mSourceUserId != UserHandle.getUserId(touchedWin.getOwningUid())){
+ mData.fixUris(mSourceUserId);
+ }
final int myPid = Process.myPid();
final IBinder token = touchedWin.mClient.asBinder();
DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 0979cd3..e229c5e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -37,8 +37,8 @@
static final boolean DEBUG = false;
static final boolean DEBUG_ADD_REMOVE = false;
static final boolean DEBUG_FOCUS = false;
- static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false;
- static final boolean DEBUG_ANIM = false;
+ static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || true;
+ static final boolean DEBUG_ANIM = true;
static final boolean DEBUG_KEYGUARD = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_LAYERS = false;
@@ -50,7 +50,7 @@
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_ORIENTATION = false;
static final boolean DEBUG_CONFIGURATION = false;
- static final boolean DEBUG_APP_TRANSITIONS = false;
+ static final boolean DEBUG_APP_TRANSITIONS = true;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d1ffaa0..d54e9e3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -769,7 +769,7 @@
mDragState.mUid,
dropTargetWin.getOwningPackage(),
mDragState.mFlags & DRAG_FLAGS_URI_PERMISSIONS,
- UserHandle.getUserId(mDragState.mUid),
+ mDragState.mSourceUserId,
UserHandle.getUserId(dropTargetWin.getOwningUid()));
}
@@ -1192,7 +1192,7 @@
break;
}
}
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"Based on layer: Adding window " + win + " at " + (i + 1) + " of "
+ windows.size());
windows.add(i + 1, win);
@@ -1224,7 +1224,7 @@
//apptoken note that the window could be a floating window
//that was created later or a window at the top of the list of
//windows associated with this token.
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"not Base app: Adding window " + win + " at " + (newIdx + 1) + " of "
+ windows.size());
windows.add(newIdx + 1, win);
@@ -1262,7 +1262,7 @@
}
}
i++;
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"Free window: Adding window " + win + " at " + i + " of " + windows.size());
windows.add(i, win);
mWindowsChanged = true;
@@ -1333,7 +1333,7 @@
}
private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
- if (DEBUG_FOCUS_LIGHT) Slog.d(TAG_WM, "addWindowToListInOrderLocked: win=" + win +
+ if (DEBUG_FOCUS) Slog.d(TAG_WM, "addWindowToListInOrderLocked: win=" + win +
" Callers=" + Debug.getCallers(4));
if (win.mAttachedWindow == null) {
final WindowToken token = win.mToken;
@@ -4236,7 +4236,6 @@
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
- wtoken.mAppStopped = false;
wtoken.waitingToShow = false;
wtoken.hiddenRequested = !visible;
@@ -4246,6 +4245,8 @@
// if made visible again.
wtoken.appDied = false;
wtoken.removeAllWindows();
+ } else if (visible) {
+ wtoken.mAppStopped = false;
}
// If we are preparing an app transition, then delay changing
@@ -4863,6 +4864,7 @@
}
}
+ @Override
public void getStackBounds(int stackId, Rect bounds) {
synchronized (mWindowMap) {
final TaskStack stack = mStackIdToStack.get(stackId);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2096ea0..5b9206d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2088,7 +2088,8 @@
return mTmpRect;
}
- private int getStackId() {
+ @Override
+ public int getStackId() {
final TaskStack stack = getStack();
if (stack == null) {
return INVALID_STACK_ID;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0201296..f8f8363 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1347,7 +1347,7 @@
}
} else {
if (DEBUG_ANIM && isAnimating()) {
- Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
+ //Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
}
displayed = true;
}
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index b72cf4d..656c214 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -330,7 +330,7 @@
jsize len = env->GetArrayLength(body);
message.length = MIN(len, CEC_MESSAGE_BODY_MAX_LENGTH);
ScopedByteArrayRO bodyPtr(env, body);
- std::memcpy(message.body, bodyPtr.get(), len);
+ std::memcpy(message.body, bodyPtr.get(), message.length);
HdmiCecController* controller =
reinterpret_cast<HdmiCecController*>(controllerPtr);
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index e75775f..cdd5519 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -68,13 +68,14 @@
static const GpsNavigationMessageInterface* sGpsNavigationMessageInterface = NULL;
static const GnssConfigurationInterface* sGnssConfigurationInterface = NULL;
-#define MAX_SATELLITE_COUNT 512
-#define MAX_GPS_SATELLITE_COUNT 512
+#define GPS_MAX_SATELLITE_COUNT 32
+#define GNSS_MAX_SATELLITE_COUNT 64
-#define PRN_SHIFT_WIDTH 3
+#define SVID_SHIFT_WIDTH 7
+#define CONSTELLATION_TYPE_SHIFT_WIDTH 3
// temporary storage for GPS callbacks
-static GnssSvInfo sGnssSvList[MAX_SATELLITE_COUNT];
+static GnssSvInfo sGnssSvList[GNSS_MAX_SATELLITE_COUNT];
static size_t sGnssSvListSize;
static const char* sNmeaString;
static int sNmeaStringLength;
@@ -113,56 +114,75 @@
{
JNIEnv* env = AndroidRuntime::getJNIEnv();
size_t status_size = sv_status->size;
- // Some drive doesn't set the size field correctly. Assume GpsSvStatus_v1 if
- // it doesn't provide a valid size.
+ // Some drives doesn't set the size field correctly. Assume GpsSvStatus_v1
+ // if it doesn't provide a valid size.
if (status_size == 0) {
- status_size = sizeof(GpsSvStatus_v1);
+ ALOGW("Invalid size of GpsSvStatus found: %zd.", status_size);
}
- if (status_size == sizeof(GpsSvStatus)) {
- sGnssSvListSize = sv_status->gnss_sv_list_size;
- // Cramp the list size
- if (sGnssSvListSize > MAX_SATELLITE_COUNT) {
- sGnssSvListSize = MAX_SATELLITE_COUNT;
- }
- // Copy GNSS SV info into sGnssSvList, if any.
- if (sGnssSvListSize > 0 && sv_status->gnss_sv_list) {
- memcpy(sGnssSvList, sv_status->gnss_sv_list, sizeof(GnssSvInfo) * sGnssSvListSize);
- }
- } else if (status_size == sizeof(GpsSvStatus_v1)) {
- sGnssSvListSize = sv_status->num_svs;
- // Cramp the list size
- if (sGnssSvListSize > MAX_GPS_SATELLITE_COUNT) {
- sGnssSvListSize = MAX_GPS_SATELLITE_COUNT;
- }
- uint32_t ephemeris_mask = sv_status->ephemeris_mask;
- uint32_t almanac_mask = sv_status->almanac_mask;
- uint32_t used_in_fix_mask = sv_status->used_in_fix_mask;
- for (size_t i = 0; i < sGnssSvListSize; i++) {
- GnssSvInfo& info = sGnssSvList[i];
+ sGnssSvListSize = sv_status->num_svs;
+ // Clamp the list size. Legacy GpsSvStatus has only 32 elements in sv_list.
+ if (sGnssSvListSize > GPS_MAX_SATELLITE_COUNT) {
+ ALOGW("Too many satellites %zd. Clamps to %d.",
+ sGnssSvListSize,
+ GPS_MAX_SATELLITE_COUNT);
+ sGnssSvListSize = GPS_MAX_SATELLITE_COUNT;
+ }
+ uint32_t ephemeris_mask = sv_status->ephemeris_mask;
+ uint32_t almanac_mask = sv_status->almanac_mask;
+ uint32_t used_in_fix_mask = sv_status->used_in_fix_mask;
+ for (size_t i = 0; i < sGnssSvListSize; i++) {
+ GnssSvInfo& info = sGnssSvList[i];
+ info.svid = sv_status->sv_list[i].prn;
+ // TODO: implement the correct logic to derive the constellation type
+ // based on PRN ranges.
+ if (info.svid >=1 && info.svid <= 32) {
info.constellation = GNSS_CONSTELLATION_GPS;
- info.prn = sv_status->sv_list[i].prn;
- info.snr = sv_status->sv_list[i].snr;
- info.elevation = sv_status->sv_list[i].elevation;
- info.azimuth = sv_status->sv_list[i].azimuth;
- info.flags = GNSS_SV_FLAGS_NONE;
- if (info.prn > 0 && info.prn <= 32) {
- int32_t this_prn_mask = (1 << (info.prn - 1));
- if ((ephemeris_mask & this_prn_mask) != 0) {
+ } else {
+ info.constellation = GNSS_CONSTELLATION_UNKNOWN;
+ }
+ info.snr = sv_status->sv_list[i].snr;
+ info.elevation = sv_status->sv_list[i].elevation;
+ info.azimuth = sv_status->sv_list[i].azimuth;
+ info.flags = GNSS_SV_FLAGS_NONE;
+ if (info.svid > 0 && info.svid <= 32) {
+ int32_t this_svid_mask = (1 << (info.svid - 1));
+ if ((ephemeris_mask & this_svid_mask) != 0) {
info.flags |= GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA;
- }
- if ((almanac_mask & this_prn_mask) != 0) {
+ }
+ if ((almanac_mask & this_svid_mask) != 0) {
info.flags |= GNSS_SV_FLAGS_HAS_ALMANAC_DATA;
- }
- if ((used_in_fix_mask & this_prn_mask) != 0) {
+ }
+ if ((used_in_fix_mask & this_svid_mask) != 0) {
info.flags |= GNSS_SV_FLAGS_USED_IN_FIX;
- }
}
}
- } else {
- sGnssSvListSize = 0;
- ALOGE("Invalid size of GpsSvStatus found: %zd.", status_size);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+static void gnss_sv_status_callback(GnssSvStatus* sv_status) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ size_t status_size = sv_status->size;
+ // Check the size, and reject the object that has invalid size.
+ if (status_size != sizeof(GnssSvStatus)) {
+ ALOGE("Invalid size of GnssSvStatus found: %zd.", status_size);
return;
}
+ sGnssSvListSize = sv_status->num_svs;
+ // Clamp the list size
+ if (sGnssSvListSize > GNSS_MAX_SATELLITE_COUNT) {
+ ALOGD("Too many satellites %zd. Clamps to %d.",
+ sGnssSvListSize,
+ GNSS_MAX_SATELLITE_COUNT);
+ sGnssSvListSize = GNSS_MAX_SATELLITE_COUNT;
+ }
+ // Copy GNSS SV info into sGnssSvList, if any.
+ if (sGnssSvListSize > 0) {
+ memcpy(sGnssSvList,
+ sv_status->gnss_sv_list,
+ sizeof(GnssSvInfo) * sGnssSvListSize);
+ }
env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
@@ -228,6 +248,7 @@
create_thread_callback,
request_utc_time_callback,
set_system_info_callback,
+ gnss_sv_status_callback,
};
static void xtra_download_request_callback()
@@ -677,31 +698,30 @@
}
static jint android_location_GnssLocationProvider_read_sv_status(JNIEnv* env, jobject /* obj */,
- jintArray prnWithFlagArray, jfloatArray snrArray, jfloatArray elevArray,
- jfloatArray azumArray, jintArray constellationTypeArray)
+ jintArray svidWithFlagArray, jfloatArray snrArray, jfloatArray elevArray,
+ jfloatArray azumArray)
{
// this should only be called from within a call to reportSvStatus
- jint* prnWithFlags = env->GetIntArrayElements(prnWithFlagArray, 0);
+ jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
jfloat* snrs = env->GetFloatArrayElements(snrArray, 0);
jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
jfloat* azim = env->GetFloatArrayElements(azumArray, 0);
- jint* constellationTypes = env->GetIntArrayElements(constellationTypeArray, 0);
// GNSS SV info.
for (size_t i = 0; i < sGnssSvListSize; ++i) {
const GnssSvInfo& info = sGnssSvList[i];
- constellationTypes[i] = info.constellation;
- prnWithFlags[i] = (info.prn << PRN_SHIFT_WIDTH) | info.flags;
+ svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
+ (info.constellation << CONSTELLATION_TYPE_SHIFT_WIDTH) |
+ info.flags;
snrs[i] = info.snr;
elev[i] = info.elevation;
azim[i] = info.azimuth;
}
- env->ReleaseIntArrayElements(prnWithFlagArray, prnWithFlags, 0);
+ env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
env->ReleaseFloatArrayElements(snrArray, snrs, 0);
env->ReleaseFloatArrayElements(elevArray, elev, 0);
env->ReleaseFloatArrayElements(azumArray, azim, 0);
- env->ReleaseIntArrayElements(constellationTypeArray, constellationTypes, 0);
return (jint) sGnssSvListSize;
}
@@ -968,370 +988,341 @@
return JNI_FALSE;
}
-static jobject translate_gps_clock(JNIEnv* env, void* data, size_t size) {
- const char* doubleSignature = "(D)V";
- const char* longSignature = "(J)V";
+template<class T>
+class JavaMethodHelper {
+ public:
+ // Helper function to call setter on a Java object.
+ static void callJavaMethod(
+ JNIEnv* env,
+ jclass clazz,
+ jobject object,
+ const char* method_name,
+ T value);
- GpsClock* clock = reinterpret_cast<GpsClock*>(data);
+ private:
+ static const char *const signature_;
+};
- jclass gpsClockClass = env->FindClass("android/location/GnssClock");
- jmethodID gpsClockCtor = env->GetMethodID(gpsClockClass, "<init>", "()V");
+template<class T>
+void JavaMethodHelper<T>::callJavaMethod(
+ JNIEnv* env,
+ jclass clazz,
+ jobject object,
+ const char* method_name,
+ T value) {
+ jmethodID method = env->GetMethodID(clazz, method_name, signature_);
+ env->CallVoidMethod(object, method, value);
+}
- jobject gpsClockObject = env->NewObject(gpsClockClass, gpsClockCtor);
+class JavaObject {
+ public:
+ JavaObject(JNIEnv* env, const char* class_name);
+ virtual ~JavaObject();
+
+ template<class T>
+ void callSetter(const char* method_name, T value);
+ template<class T>
+ void callSetter(const char* method_name, T* value, size_t size);
+ jobject get();
+
+ private:
+ JNIEnv* env_;
+ jclass clazz_;
+ jobject object_;
+};
+
+JavaObject::JavaObject(JNIEnv* env, const char* class_name) : env_(env) {
+ clazz_ = env_->FindClass(class_name);
+ jmethodID ctor = env->GetMethodID(clazz_, "<init>", "()V");
+ object_ = env_->NewObject(clazz_, ctor);
+}
+
+JavaObject::~JavaObject() {
+ env_->DeleteLocalRef(clazz_);
+}
+
+template<class T>
+void JavaObject::callSetter(const char* method_name, T value) {
+ JavaMethodHelper<T>::callJavaMethod(
+ env_, clazz_, object_, method_name, value);
+}
+
+template<>
+void JavaObject::callSetter(
+ const char* method_name, uint8_t* value, size_t size) {
+ jbyteArray array = env_->NewByteArray(size);
+ env_->SetByteArrayRegion(array, 0, size, (jbyte*) value);
+ jmethodID method = env_->GetMethodID(
+ clazz_,
+ method_name,
+ "([B)V");
+ env_->CallVoidMethod(object_, method, array);
+}
+
+jobject JavaObject::get() {
+ return object_;
+}
+
+// Define Java method signatures for all known types.
+
+template<>
+const char *const JavaMethodHelper<uint8_t>::signature_ = "(B)V";
+template<>
+const char *const JavaMethodHelper<int8_t>::signature_ = "(B)V";
+template<>
+const char *const JavaMethodHelper<int16_t>::signature_ = "(S)V";
+template<>
+const char *const JavaMethodHelper<uint16_t>::signature_ = "(S)V";
+template<>
+const char *const JavaMethodHelper<int>::signature_ = "(I)V";
+template<>
+const char *const JavaMethodHelper<int64_t>::signature_ = "(J)V";
+template<>
+const char *const JavaMethodHelper<float>::signature_ = "(F)V";
+template<>
+const char *const JavaMethodHelper<double>::signature_ = "(D)V";
+template<>
+const char *const JavaMethodHelper<bool>::signature_ = "(Z)V";
+
+#define SET(setter, value) object.callSetter("set" # setter, (value))
+#define SET_IF(flag, setter, value) \
+ if (flags & (flag)) object.callSetter("set" # setter, (value))
+
+static jobject translate_gps_clock(JNIEnv* env, GpsClock* clock) {
+ JavaObject object(env, "android/location/GnssClock");
GpsClockFlags flags = clock->flags;
- if (flags & GPS_CLOCK_HAS_LEAP_SECOND) {
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setLeapSecond", "(S)V");
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->leap_second);
- }
+ SET_IF(GPS_CLOCK_HAS_LEAP_SECOND, LeapSecond, clock->leap_second);
+ SET(Type, clock->type);
+ SET(TimeInNs, clock->time_ns);
+ SET_IF(GPS_CLOCK_HAS_TIME_UNCERTAINTY,
+ TimeUncertaintyInNs,
+ clock->time_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_FULL_BIAS, FullBiasInNs, clock->full_bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS, BiasInNs, clock->bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS_UNCERTAINTY,
+ BiasUncertaintyInNs,
+ clock->bias_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_DRIFT, DriftInNsPerSec, clock->drift_nsps);
+ SET_IF(GPS_CLOCK_HAS_DRIFT_UNCERTAINTY,
+ DriftUncertaintyInNsPerSec,
+ clock->drift_uncertainty_nsps);
- jmethodID typeSetterMethod = env->GetMethodID(gpsClockClass, "setType", "(B)V");
- env->CallVoidMethod(gpsClockObject, typeSetterMethod, clock->type);
-
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setTimeInNs", longSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->time_ns);
-
- if (flags & GPS_CLOCK_HAS_TIME_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setTimeUncertaintyInNs", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->time_uncertainty_ns);
- }
-
- if (flags & GPS_CLOCK_HAS_FULL_BIAS) {
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setFullBiasInNs", longSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->full_bias_ns);
- }
-
- if (flags & GPS_CLOCK_HAS_BIAS) {
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setBiasInNs", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->bias_ns);
- }
-
- if (flags & GPS_CLOCK_HAS_BIAS_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setBiasUncertaintyInNs", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->bias_uncertainty_ns);
- }
-
- if (flags & GPS_CLOCK_HAS_DRIFT) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setDriftInNsPerSec", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->drift_nsps);
- }
-
- if (flags & GPS_CLOCK_HAS_DRIFT_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setDriftUncertaintyInNsPerSec", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->drift_uncertainty_nsps);
- }
-
+ /*
if (flags & GPS_CLOCK_TYPE_LOCAL_HW_TIME) {
- if (size == sizeof(GpsClock)) {
+ if (size == sizeof(GnssClock)) {
jmethodID setterMethod =
env->GetMethodID(gpsClockClass,
"setTimeOfLastHwClockDiscontinuityInNs",
longSignature);
env->CallVoidMethod(gpsClockObject,
setterMethod,
- clock->time_of_last_hw_clock_discontinuity_ns);
+ reinterpret_cast<GnssClock*>(clock)->time_of_last_hw_clock_discontinuity_ns);
}
}
+ */
- env->DeleteLocalRef(gpsClockClass);
- return gpsClockObject;
+ return object.get();
}
-static jobject translate_gps_measurement(JNIEnv* env, void* data, size_t size) {
- const char* byteSignature = "(B)V";
- const char* shortSignature = "(S)V";
- const char* intSignature = "(I)V";
- const char* longSignature = "(J)V";
- const char* floatSignature = "(F)V";
- const char* doubleSignature = "(D)V";
+static jobject translate_gnss_clock(JNIEnv* env, GnssClock* clock) {
+ JavaObject object(env, "android/location/GnssClock");
+ GpsClockFlags flags = clock->flags;
- jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
- jmethodID gnssMeasurementCtor = env->GetMethodID(gnssMeasurementClass, "<init>", "()V");
- GpsMeasurement* measurement = reinterpret_cast<GpsMeasurement*>(data);
+ SET_IF(GPS_CLOCK_HAS_LEAP_SECOND, LeapSecond, clock->leap_second);
+ SET(Type, clock->type);
+ SET(TimeInNs, clock->time_ns);
+ SET_IF(GPS_CLOCK_HAS_TIME_UNCERTAINTY,
+ TimeUncertaintyInNs,
+ clock->time_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_FULL_BIAS, FullBiasInNs, clock->full_bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS, BiasInNs, clock->bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS_UNCERTAINTY,
+ BiasUncertaintyInNs,
+ clock->bias_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_DRIFT, DriftInNsPerSec, clock->drift_nsps);
+ SET_IF(GPS_CLOCK_HAS_DRIFT_UNCERTAINTY,
+ DriftUncertaintyInNsPerSec,
+ clock->drift_uncertainty_nsps);
- jobject gnssMeasurementObject = env->NewObject(gnssMeasurementClass, gnssMeasurementCtor);
+ SET_IF(GPS_CLOCK_TYPE_LOCAL_HW_TIME,
+ TimeOfLastHwClockDiscontinuityInNs,
+ clock->time_of_last_hw_clock_discontinuity_ns);
+
+ return object.get();
+}
+
+static jobject translate_gps_measurement(JNIEnv* env,
+ GpsMeasurement* measurement) {
+ JavaObject object(env, "android/location/GnssMeasurement");
GpsMeasurementFlags flags = measurement->flags;
- jmethodID prnSetterMethod = env->GetMethodID(gnssMeasurementClass, "setPrn", byteSignature);
- env->CallVoidMethod(gnssMeasurementObject, prnSetterMethod, measurement->prn);
+ SET(Svid, static_cast<int16_t>(measurement->prn));
+ SET(TimeOffsetInNs, measurement->time_offset_ns);
+ SET(State, measurement->state);
+ SET(ReceivedGpsTowInNs, measurement->received_gps_tow_ns);
+ SET(ReceivedGpsTowUncertaintyInNs,
+ measurement->received_gps_tow_uncertainty_ns);
+ SET(Cn0InDbHz, measurement->c_n0_dbhz);
+ SET(PseudorangeRateInMetersPerSec, measurement->pseudorange_rate_mps);
+ SET(PseudorangeRateUncertaintyInMetersPerSec,
+ measurement->pseudorange_rate_uncertainty_mps);
+ SET(AccumulatedDeltaRangeState, measurement->accumulated_delta_range_state);
+ SET(AccumulatedDeltaRangeInMeters, measurement->accumulated_delta_range_m);
+ SET(AccumulatedDeltaRangeUncertaintyInMeters,
+ measurement->accumulated_delta_range_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE,
+ PseudorangeInMeters,
+ measurement->pseudorange_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY,
+ PseudorangeUncertaintyInMeters,
+ measurement->pseudorange_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE,
+ CodePhaseInChips,
+ measurement->code_phase_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY,
+ CodePhaseUncertaintyInChips,
+ measurement->code_phase_uncertainty_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY,
+ CarrierFrequencyInHz,
+ measurement->carrier_frequency_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_CYCLES,
+ CarrierCycles,
+ measurement->carrier_cycles);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE,
+ CarrierPhase,
+ measurement->carrier_phase);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY,
+ CarrierPhaseUncertainty,
+ measurement->carrier_phase_uncertainty);
+ SET(LossOfLock, measurement->loss_of_lock);
+ SET_IF(GPS_MEASUREMENT_HAS_BIT_NUMBER, BitNumber, measurement->bit_number);
+ SET_IF(GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT,
+ TimeFromLastBitInMs,
+ measurement->time_from_last_bit_ms);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT,
+ DopplerShiftInHz,
+ measurement->doppler_shift_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY,
+ DopplerShiftUncertaintyInHz,
+ measurement->doppler_shift_uncertainty_hz);
+ SET(MultipathIndicator, measurement->multipath_indicator);
+ SET_IF(GPS_MEASUREMENT_HAS_SNR, SnrInDb, measurement->snr_db);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION,
+ ElevationInDeg,
+ measurement->elevation_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY,
+ ElevationUncertaintyInDeg,
+ measurement->elevation_uncertainty_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH,
+ AzimuthInDeg,
+ measurement->azimuth_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY,
+ AzimuthUncertaintyInDeg,
+ measurement->azimuth_uncertainty_deg);
+ SET(UsedInFix,
+ (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
- jmethodID timeOffsetSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setTimeOffsetInNs", doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- timeOffsetSetterMethod,
- measurement->time_offset_ns);
-
- jmethodID stateSetterMethod = env->GetMethodID(gnssMeasurementClass, "setState", shortSignature);
- env->CallVoidMethod(gnssMeasurementObject, stateSetterMethod, measurement->state);
-
- jmethodID receivedGpsTowSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setReceivedGpsTowInNs", longSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- receivedGpsTowSetterMethod,
- measurement->received_gps_tow_ns);
-
- jmethodID receivedGpsTowUncertaintySetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setReceivedGpsTowUncertaintyInNs",
- longSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- receivedGpsTowUncertaintySetterMethod,
- measurement->received_gps_tow_uncertainty_ns);
-
- jmethodID cn0SetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCn0InDbHz", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, cn0SetterMethod, measurement->c_n0_dbhz);
-
- jmethodID pseudorangeRateSetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setPseudorangeRateInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- pseudorangeRateSetterMethod,
- measurement->pseudorange_rate_mps);
-
- jmethodID pseudorangeRateUncertaintySetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setPseudorangeRateUncertaintyInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- pseudorangeRateUncertaintySetterMethod,
- measurement->pseudorange_rate_uncertainty_mps);
-
- jmethodID accumulatedDeltaRangeStateSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setAccumulatedDeltaRangeState", shortSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- accumulatedDeltaRangeStateSetterMethod,
- measurement->accumulated_delta_range_state);
-
- jmethodID accumulatedDeltaRangeSetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setAccumulatedDeltaRangeInMeters",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- accumulatedDeltaRangeSetterMethod,
- measurement->accumulated_delta_range_m);
-
- jmethodID accumulatedDeltaRangeUncertaintySetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setAccumulatedDeltaRangeUncertaintyInMeters",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- accumulatedDeltaRangeUncertaintySetterMethod,
- measurement->accumulated_delta_range_uncertainty_m);
-
- if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setPseudorangeInMeters", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->pseudorange_m);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setPseudorangeUncertaintyInMeters",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->pseudorange_uncertainty_m);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCodePhaseInChips", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->code_phase_chips);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setCodePhaseUncertaintyInChips",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->code_phase_uncertainty_chips);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCarrierFrequencyInHz", floatSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->carrier_frequency_hz);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_CYCLES) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCarrierCycles", longSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->carrier_cycles);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_PHASE) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCarrierPhase", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->carrier_phase);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setCarrierPhaseUncertainty",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->carrier_phase_uncertainty);
- }
-
- jmethodID lossOfLockSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setLossOfLock", byteSignature);
- env->CallVoidMethod(gnssMeasurementObject, lossOfLockSetterMethod, measurement->loss_of_lock);
-
- if (flags & GPS_MEASUREMENT_HAS_BIT_NUMBER) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setBitNumber", intSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->bit_number);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setTimeFromLastBitInMs", shortSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->time_from_last_bit_ms);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setDopplerShiftInHz", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->doppler_shift_hz);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setDopplerShiftUncertaintyInHz",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->doppler_shift_uncertainty_hz);
- }
-
- jmethodID multipathIndicatorSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setMultipathIndicator", byteSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- multipathIndicatorSetterMethod,
- measurement->multipath_indicator);
-
- if (flags & GPS_MEASUREMENT_HAS_SNR) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setSnrInDb", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->snr_db);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_ELEVATION) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setElevationInDeg", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->elevation_deg);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setElevationUncertaintyInDeg", doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->elevation_uncertainty_deg);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_AZIMUTH) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setAzimuthInDeg", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->azimuth_deg);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setAzimuthUncertaintyInDeg",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->azimuth_uncertainty_deg);
- }
-
- jmethodID usedInFixSetterMethod = env->GetMethodID(gnssMeasurementClass, "setUsedInFix", "(Z)V");
- env->CallVoidMethod(
- gnssMeasurementObject,
- usedInFixSetterMethod,
- (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
-
- if (size == sizeof(GpsMeasurement)) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass,
- "setPseudorangeRateCarrierInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->pseudorange_rate_carrier_mps);
-
- setterMethod =
- env->GetMethodID(gnssMeasurementClass,
- "setPseudorangeRateCarrierUncertaintyInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->pseudorange_rate_carrier_uncertainty_mps);
- }
-
- env->DeleteLocalRef(gnssMeasurementClass);
- return gnssMeasurementObject;
+ return object.get();
}
-/**
- * <T> can only be GpsData or GpsData_v1. Must rewrite this function if more
- * types are introduced in the future releases.
- */
-template<class T>
-static jobjectArray translate_gps_measurements(JNIEnv* env, void* data) {
- T* gps_data = reinterpret_cast<T*>(data);
- size_t measurementCount = gps_data->measurement_count;
- if (measurementCount == 0) {
+static jobject translate_gnss_measurement(JNIEnv* env,
+ GnssMeasurement* measurement) {
+ JavaObject object(env, "android/location/GnssMeasurement");
+ GpsMeasurementFlags flags = measurement->flags;
+
+ SET(Svid, measurement->svid);
+ SET(TimeOffsetInNs, measurement->time_offset_ns);
+ SET(State, measurement->state);
+ SET(ReceivedGpsTowInNs, measurement->received_gps_tow_ns);
+ SET(ReceivedGpsTowUncertaintyInNs,
+ measurement->received_gps_tow_uncertainty_ns);
+ SET(Cn0InDbHz, measurement->c_n0_dbhz);
+ SET(PseudorangeRateInMetersPerSec, measurement->pseudorange_rate_mps);
+ SET(PseudorangeRateUncertaintyInMetersPerSec,
+ measurement->pseudorange_rate_uncertainty_mps);
+ SET(AccumulatedDeltaRangeState, measurement->accumulated_delta_range_state);
+ SET(AccumulatedDeltaRangeInMeters, measurement->accumulated_delta_range_m);
+ SET(AccumulatedDeltaRangeUncertaintyInMeters,
+ measurement->accumulated_delta_range_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE,
+ PseudorangeInMeters,
+ measurement->pseudorange_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY,
+ PseudorangeUncertaintyInMeters,
+ measurement->pseudorange_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE,
+ CodePhaseInChips,
+ measurement->code_phase_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY,
+ CodePhaseUncertaintyInChips,
+ measurement->code_phase_uncertainty_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY,
+ CarrierFrequencyInHz,
+ measurement->carrier_frequency_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_CYCLES,
+ CarrierCycles,
+ measurement->carrier_cycles);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE,
+ CarrierPhase,
+ measurement->carrier_phase);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY,
+ CarrierPhaseUncertainty,
+ measurement->carrier_phase_uncertainty);
+ SET(LossOfLock, measurement->loss_of_lock);
+ SET_IF(GPS_MEASUREMENT_HAS_BIT_NUMBER, BitNumber, measurement->bit_number);
+ SET_IF(GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT,
+ TimeFromLastBitInMs,
+ measurement->time_from_last_bit_ms);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT,
+ DopplerShiftInHz,
+ measurement->doppler_shift_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY,
+ DopplerShiftUncertaintyInHz,
+ measurement->doppler_shift_uncertainty_hz);
+ SET(MultipathIndicator, measurement->multipath_indicator);
+ SET_IF(GPS_MEASUREMENT_HAS_SNR, SnrInDb, measurement->snr_db);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION,
+ ElevationInDeg,
+ measurement->elevation_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY,
+ ElevationUncertaintyInDeg,
+ measurement->elevation_uncertainty_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH,
+ AzimuthInDeg,
+ measurement->azimuth_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY,
+ AzimuthUncertaintyInDeg,
+ measurement->azimuth_uncertainty_deg);
+ SET(UsedInFix,
+ (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
+
+ SET(PseudorangeRateCarrierInMetersPerSec,
+ measurement->pseudorange_rate_carrier_mps);
+ SET(PseudorangeRateCarrierUncertaintyInMetersPerSec,
+ measurement->pseudorange_rate_carrier_uncertainty_mps);
+
+ return object.get();
+}
+
+static jobjectArray translate_gps_measurements(JNIEnv* env,
+ GpsMeasurement* measurements,
+ size_t count) {
+ if (count == 0) {
return NULL;
}
- jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
+ jclass gnssMeasurementClass = env->FindClass(
+ "android/location/GnssMeasurement");
jobjectArray gnssMeasurementArray = env->NewObjectArray(
- measurementCount,
+ count,
gnssMeasurementClass,
NULL /* initialElement */);
- for (uint16_t i = 0; i < measurementCount; ++i) {
+ for (uint16_t i = 0; i < count; ++i) {
jobject gnssMeasurement = translate_gps_measurement(
env,
- &(gps_data->measurements[i]),
- sizeof(gps_data->measurements[0]));
+ &measurements[i]);
env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement);
env->DeleteLocalRef(gnssMeasurement);
}
@@ -1340,27 +1331,37 @@
return gnssMeasurementArray;
}
-static void measurement_callback(GpsData* data) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- if (data == NULL) {
- ALOGE("Invalid data provided to gps_measurement_callback");
- return;
- }
- if (data->size != sizeof(GpsData) && data->size != sizeof(GpsData_v1)) {
- ALOGE("Invalid GpsData size found in gps_measurement_callback, size=%zd", data->size);
- return;
+static jobjectArray translate_gnss_measurements(JNIEnv* env,
+ GnssMeasurement* measurements,
+ size_t count) {
+ if (count == 0) {
+ return NULL;
}
- jobject gpsClock;
- jobjectArray measurementArray;
- if (data->size == sizeof(GpsData)) {
- gpsClock = translate_gps_clock(env, &data->clock, sizeof(GpsClock));
- measurementArray = translate_gps_measurements<GpsData>(env, data);
- } else {
- gpsClock = translate_gps_clock(env, &data->clock, sizeof(GpsClock_v1));
- measurementArray = translate_gps_measurements<GpsData_v1>(env, data);
+ jclass gnssMeasurementClass = env->FindClass(
+ "android/location/GnssMeasurement");
+ jobjectArray gnssMeasurementArray = env->NewObjectArray(
+ count,
+ gnssMeasurementClass,
+ NULL /* initialElement */);
+
+ for (uint16_t i = 0; i < count; ++i) {
+ jobject gnssMeasurement = translate_gnss_measurement(
+ env,
+ &measurements[i]);
+ env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement);
+ env->DeleteLocalRef(gnssMeasurement);
}
- jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent");
+
+ env->DeleteLocalRef(gnssMeasurementClass);
+ return gnssMeasurementArray;
+}
+
+static void set_measurement_data(JNIEnv *env,
+ jobject clock,
+ jobjectArray measurementArray) {
+ jclass gnssMeasurementsEventClass = env->FindClass(
+ "android/location/GnssMeasurementsEvent");
jmethodID gnssMeasurementsEventCtor = env->GetMethodID(
gnssMeasurementsEventClass,
"<init>",
@@ -1369,21 +1370,68 @@
jobject gnssMeasurementsEvent = env->NewObject(
gnssMeasurementsEventClass,
gnssMeasurementsEventCtor,
- gpsClock,
+ clock,
measurementArray);
-
- env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, gnssMeasurementsEvent);
+ env->CallVoidMethod(mCallbacksObj,
+ method_reportMeasurementData,
+ gnssMeasurementsEvent);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
-
- env->DeleteLocalRef(gpsClock);
- env->DeleteLocalRef(measurementArray);
env->DeleteLocalRef(gnssMeasurementsEventClass);
env->DeleteLocalRef(gnssMeasurementsEvent);
}
+static void measurement_callback(GpsData* data) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (data == NULL) {
+ ALOGE("Invalid data provided to gps_measurement_callback");
+ return;
+ }
+ if (data->size != sizeof(GpsData)) {
+ ALOGE("Invalid GpsData size found in gps_measurement_callback, "
+ "size=%zd",
+ data->size);
+ return;
+ }
+
+ jobject clock;
+ jobjectArray measurementArray;
+ clock = translate_gps_clock(env, &data->clock);
+ measurementArray = translate_gps_measurements(
+ env, data->measurements, data->measurement_count);
+ set_measurement_data(env, clock, measurementArray);
+
+ env->DeleteLocalRef(clock);
+ env->DeleteLocalRef(measurementArray);
+}
+
+static void gnss_measurement_callback(GnssData* data) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (data == NULL) {
+ ALOGE("Invalid data provided to gps_measurement_callback");
+ return;
+ }
+ if (data->size != sizeof(GpsData)) {
+ ALOGE("Invalid GpsData size found in gps_measurement_callback, "
+ "size=%zd",
+ data->size);
+ return;
+ }
+
+ jobject clock;
+ jobjectArray measurementArray;
+ clock = translate_gnss_clock(env, &data->clock);
+ measurementArray = translate_gnss_measurements(
+ env, data->measurements, data->measurement_count);
+ set_measurement_data(env, clock, measurementArray);
+
+ env->DeleteLocalRef(clock);
+ env->DeleteLocalRef(measurementArray);
+}
+
GpsMeasurementCallbacks sGpsMeasurementCallbacks = {
sizeof(GpsMeasurementCallbacks),
measurement_callback,
+ gnss_measurement_callback,
};
static jboolean android_location_GnssLocationProvider_is_measurement_supported(
@@ -1431,69 +1479,86 @@
ALOGE("Invalid Navigation Message found: data=%p, length=%zd", data, dataLength);
return NULL;
}
+ JavaObject object(env, "android/location/GnssNavigationMessage");
+ SET(Type, message->type);
+ SET(Svid, static_cast<int16_t>(message->prn));
+ SET(MessageId, message->message_id);
+ SET(SubmessageId, message->submessage_id);
+ object.callSetter("setData", data, dataLength);
+ return object.get();
+}
- jclass navigationMessageClass = env->FindClass("android/location/GnssNavigationMessage");
- jmethodID navigationMessageCtor = env->GetMethodID(navigationMessageClass, "<init>", "()V");
- jobject navigationMessageObject = env->NewObject(navigationMessageClass, navigationMessageCtor);
+static jobject translate_gnss_navigation_message(
+ JNIEnv* env, GnssNavigationMessage* message) {
+ size_t dataLength = message->data_length;
+ uint8_t* data = message->data;
+ if (dataLength == 0 || data == NULL) {
+ ALOGE("Invalid Navigation Message found: data=%p, length=%zd", data, dataLength);
+ return NULL;
+ }
+ JavaObject object(env, "android/location/GnssNavigationMessage");
+ SET(Type, message->type);
+ SET(Svid, message->svid);
+ SET(MessageId, message->message_id);
+ SET(SubmessageId, message->submessage_id);
+ object.callSetter("setData", data, dataLength);
+ return object.get();
+}
- jmethodID setTypeMethod = env->GetMethodID(navigationMessageClass, "setType", "(B)V");
- env->CallVoidMethod(navigationMessageObject, setTypeMethod, message->type);
-
- jmethodID setPrnMethod = env->GetMethodID(navigationMessageClass, "setPrn", "(B)V");
- env->CallVoidMethod(navigationMessageObject, setPrnMethod, message->prn);
-
- jmethodID setMessageIdMethod = env->GetMethodID(navigationMessageClass, "setMessageId", "(S)V");
- env->CallVoidMethod(navigationMessageObject, setMessageIdMethod, message->message_id);
-
- jmethodID setSubmessageIdMethod =
- env->GetMethodID(navigationMessageClass, "setSubmessageId", "(S)V");
- env->CallVoidMethod(navigationMessageObject, setSubmessageIdMethod, message->submessage_id);
-
- jbyteArray dataArray = env->NewByteArray(dataLength);
- env->SetByteArrayRegion(dataArray, 0, dataLength, (jbyte*) data);
- jmethodID setDataMethod = env->GetMethodID(navigationMessageClass, "setData", "([B)V");
- env->CallVoidMethod(navigationMessageObject, setDataMethod, dataArray);
-
- env->DeleteLocalRef(navigationMessageClass);
- env->DeleteLocalRef(dataArray);
- return navigationMessageObject;
+static void set_navigation_message(jobject navigationMessage) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jclass navigationMessageEventClass =
+ env->FindClass("android/location/GnssNavigationMessageEvent");
+ jmethodID navigationMessageEventCtor = env->GetMethodID(
+ navigationMessageEventClass,
+ "<init>",
+ "(Landroid/location/GnssNavigationMessage;)V");
+ jobject navigationMessageEvent = env->NewObject(
+ navigationMessageEventClass,
+ navigationMessageEventCtor,
+ navigationMessage);
+ env->CallVoidMethod(mCallbacksObj,
+ method_reportNavigationMessages,
+ navigationMessageEvent);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ env->DeleteLocalRef(navigationMessageEventClass);
+ env->DeleteLocalRef(navigationMessageEvent);
}
static void navigation_message_callback(GpsNavigationMessage* message) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
if (message == NULL) {
ALOGE("Invalid Navigation Message provided to callback");
return;
}
-
- if (message->size == sizeof(GpsNavigationMessage)) {
- jobject navigationMessage = translate_gps_navigation_message(env, message);
-
- jclass navigationMessageEventClass =
- env->FindClass("android/location/GnssNavigationMessageEvent");
- jmethodID navigationMessageEventCtor = env->GetMethodID(
- navigationMessageEventClass,
- "<init>",
- "(Landroid/location/GnssNavigationMessage;)V");
- jobject navigationMessageEvent = env->NewObject(
- navigationMessageEventClass,
- navigationMessageEventCtor,
- navigationMessage);
-
- env->CallVoidMethod(mCallbacksObj, method_reportNavigationMessages, navigationMessageEvent);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-
- env->DeleteLocalRef(navigationMessage);
- env->DeleteLocalRef(navigationMessageEventClass);
- env->DeleteLocalRef(navigationMessageEvent);
- } else {
+ if (message->size != sizeof(GpsNavigationMessage)) {
ALOGE("Invalid GpsNavigationMessage size found: %zd", message->size);
+ return;
}
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject navigationMessage = translate_gps_navigation_message(env, message);
+ set_navigation_message(navigationMessage);
+ env->DeleteLocalRef(navigationMessage);
+}
+
+static void gnss_navigation_message_callback(GnssNavigationMessage* message) {
+ if (message == NULL) {
+ ALOGE("Invalid Navigation Message provided to callback");
+ return;
+ }
+ if (message->size != sizeof(GnssNavigationMessage)) {
+ ALOGE("Invalid GnssNavigationMessage size found: %zd", message->size);
+ return;
+ }
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject navigationMessage = translate_gnss_navigation_message(env, message);
+ set_navigation_message(navigationMessage);
+ env->DeleteLocalRef(navigationMessage);
}
GpsNavigationMessageCallbacks sGpsNavigationMessageCallbacks = {
sizeof(GpsNavigationMessageCallbacks),
navigation_message_callback,
+ gnss_navigation_message_callback,
};
static jboolean android_location_GnssLocationProvider_is_navigation_message_supported(
@@ -1567,7 +1632,7 @@
"(I)V",
(void*)android_location_GnssLocationProvider_delete_aiding_data},
{"native_read_sv_status",
- "([I[F[F[F[I)I",
+ "([I[F[F[F)I",
(void*)android_location_GnssLocationProvider_read_sv_status},
{"native_read_nmea", "([BI)I", (void*)android_location_GnssLocationProvider_read_nmea},
{"native_inject_time", "(JJI)V", (void*)android_location_GnssLocationProvider_inject_time},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f0fc0b3..916b66d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3524,11 +3524,14 @@
@Override
public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) {
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
- // This API can only be called by an active device admin,
- // so try to retrieve it to check that the caller is one.
- getActiveAdminForCallerLocked(
- null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
+ if (!isCallerWithSystemUid()) {
+ // This API can only be called by an active device admin,
+ // so try to retrieve it to check that the caller is one.
+ getActiveAdminForCallerLocked(
+ null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
+ }
DevicePolicyData policy = getUserDataUnchecked(getCredentialOwner(userHandle, parent));
@@ -8038,7 +8041,8 @@
boolean isPackageInstalledForUser(String packageName, int userHandle) {
try {
- PackageInfo pi = mIPackageManager.getPackageInfo(packageName, 0, userHandle);
+ PackageInfo pi = mInjector.getIPackageManager().getPackageInfo(packageName, 0,
+ userHandle);
return (pi != null) && (pi.applicationInfo.flags != 0);
} catch (RemoteException re) {
throw new RuntimeException("Package manager has died", re);
@@ -8533,26 +8537,32 @@
mPackagesToRemove.add(packageUserPair);
}
- final List<ComponentName> activeAdminsList = getActiveAdmins(userId);
- if (activeAdminsList == null || activeAdminsList.size() == 0) {
- startUninstallIntent(packageName, userId);
- return;
- }
+ // All active admins on the user.
+ final List<ComponentName> allActiveAdmins = getActiveAdmins(userId);
- for (ComponentName activeAdmin : activeAdminsList) {
- if (packageName.equals(activeAdmin.getPackageName())) {
- removeActiveAdmin(activeAdmin, userId);
+ // Active admins in the target package.
+ final List<ComponentName> packageActiveAdmins = new ArrayList<>();
+ if (allActiveAdmins != null) {
+ for (ComponentName activeAdmin : allActiveAdmins) {
+ if (packageName.equals(activeAdmin.getPackageName())) {
+ packageActiveAdmins.add(activeAdmin);
+ removeActiveAdmin(activeAdmin, userId);
+ }
}
}
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- for (ComponentName activeAdmin : activeAdminsList) {
- removeAdminArtifacts(activeAdmin, userId);
+ if (packageActiveAdmins.size() == 0) {
+ startUninstallIntent(packageName, userId);
+ } else {
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ for (ComponentName activeAdmin : packageActiveAdmins) {
+ removeAdminArtifacts(activeAdmin, userId);
+ }
+ startUninstallIntent(packageName, userId);
}
- startUninstallIntent(packageName, userId);
- }
- }, DEVICE_ADMIN_DEACTIVATE_TIMEOUT); // Start uninstall after timeout anyway.
+ }, DEVICE_ADMIN_DEACTIVATE_TIMEOUT); // Start uninstall after timeout anyway.
+ }
}
private void removePackageIfRequired(final String packageName, final int userId) {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 2329b42..ceabfce 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -27,7 +27,6 @@
import android.content.IntentFilter;
import android.net.DhcpResults;
import android.net.BaseDhcpStateMachine;
-import android.net.DhcpStateMachine;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.NetworkUtils;
@@ -103,12 +102,35 @@
// t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
private static final int DHCP_TIMEOUT_MS = 36 * SECONDS;
+ private static final int PUBLIC_BASE = Protocol.BASE_DHCP;
+
+ /* Commands from controller to start/stop DHCP */
+ public static final int CMD_START_DHCP = PUBLIC_BASE + 1;
+ public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2;
+ public static final int CMD_RENEW_DHCP = PUBLIC_BASE + 3;
+
+ /* Notification from DHCP state machine prior to DHCP discovery/renewal */
+ public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 4;
+ /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
+ * success/failure */
+ public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 5;
+ /* Notification from DHCP state machine before quitting */
+ public static final int CMD_ON_QUIT = PUBLIC_BASE + 6;
+
+ /* Command from controller to indicate DHCP discovery/renewal can continue
+ * after pre DHCP action is complete */
+ public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 7;
+
+ /* Message.arg1 arguments to CMD_POST_DHCP notification */
+ public static final int DHCP_SUCCESS = 1;
+ public static final int DHCP_FAILURE = 2;
+
// Messages.
- private static final int BASE = Protocol.BASE_DHCP + 100;
- private static final int CMD_KICK = BASE + 1;
- private static final int CMD_RECEIVED_PACKET = BASE + 2;
- private static final int CMD_TIMEOUT = BASE + 3;
- private static final int CMD_ONESHOT_TIMEOUT = BASE + 4;
+ private static final int PRIVATE_BASE = Protocol.BASE_DHCP + 100;
+ private static final int CMD_KICK = PRIVATE_BASE + 1;
+ private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
+ private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
+ private static final int CMD_ONESHOT_TIMEOUT = PRIVATE_BASE + 4;
// DHCP parameters that we request.
private static final byte[] REQUESTED_PARAMS = new byte[] {
@@ -211,7 +233,7 @@
// Used to time out PacketRetransmittingStates.
mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
// Used to schedule DHCP renews.
- mRenewAlarm = makeWakeupMessage("RENEW", DhcpStateMachine.CMD_RENEW_DHCP);
+ mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
// Used to tell the caller when its request (CMD_START_DHCP or CMD_RENEW_DHCP) timed out.
// TODO: when the legacy DHCP client is gone, make the client fully asynchronous and
// remove this.
@@ -400,13 +422,12 @@
}
private void notifySuccess() {
- mController.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION,
- DhcpStateMachine.DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
+ mController.sendMessage(
+ CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
}
private void notifyFailure() {
- mController.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION,
- DhcpStateMachine.DHCP_FAILURE, 0, null);
+ mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
}
private void clearDhcpState() {
@@ -428,7 +449,7 @@
protected void onQuitting() {
Log.d(TAG, "onQuitting");
- mController.sendMessage(DhcpStateMachine.CMD_ON_QUIT);
+ mController.sendMessage(CMD_ON_QUIT);
}
private void maybeLog(String msg) {
@@ -442,17 +463,17 @@
private String messageName(int what) {
switch (what) {
- case DhcpStateMachine.CMD_START_DHCP:
+ case CMD_START_DHCP:
return "CMD_START_DHCP";
- case DhcpStateMachine.CMD_STOP_DHCP:
+ case CMD_STOP_DHCP:
return "CMD_STOP_DHCP";
- case DhcpStateMachine.CMD_RENEW_DHCP:
+ case CMD_RENEW_DHCP:
return "CMD_RENEW_DHCP";
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+ case CMD_PRE_DHCP_ACTION:
return "CMD_PRE_DHCP_ACTION";
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
+ case CMD_PRE_DHCP_ACTION_COMPLETE:
return "CMD_PRE_DHCP_ACTION_COMPLETE";
- case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+ case CMD_POST_DHCP_ACTION:
return "CMD_POST_DHCP_ACTION";
case CMD_KICK:
return "CMD_KICK";
@@ -495,14 +516,14 @@
@Override
public void enter() {
super.enter();
- mController.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION);
+ mController.sendMessage(CMD_PRE_DHCP_ACTION);
}
@Override
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
+ case CMD_PRE_DHCP_ACTION_COMPLETE:
transitionTo(mOtherState);
return HANDLED;
default:
@@ -532,7 +553,7 @@
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_START_DHCP:
+ case CMD_START_DHCP:
scheduleOneshotTimeout();
if (mRegisteredForPreDhcpNotification) {
transitionTo(mWaitBeforeStartState);
@@ -588,7 +609,7 @@
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_STOP_DHCP:
+ case CMD_STOP_DHCP:
transitionTo(mStoppedState);
return HANDLED;
case CMD_ONESHOT_TIMEOUT:
@@ -810,7 +831,7 @@
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_RENEW_DHCP:
+ case CMD_RENEW_DHCP:
if (mRegisteredForPreDhcpNotification) {
transitionTo(mWaitBeforeRenewalState);
} else {
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 4fe97c1..a388a26 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -258,12 +258,12 @@
return "EVENT_PRE_DHCP_ACTION_COMPLETE";
case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
return "EVENT_NETLINK_LINKPROPERTIES_CHANGED";
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
- return "DhcpStateMachine.CMD_PRE_DHCP_ACTION";
- case DhcpStateMachine.CMD_POST_DHCP_ACTION:
- return "DhcpStateMachine.CMD_POST_DHCP_ACTION";
- case DhcpStateMachine.CMD_ON_QUIT:
- return "DhcpStateMachine.CMD_ON_QUIT";
+ case DhcpClient.CMD_PRE_DHCP_ACTION:
+ return "DhcpClient.CMD_PRE_DHCP_ACTION";
+ case DhcpClient.CMD_POST_DHCP_ACTION:
+ return "DhcpClient.CMD_POST_DHCP_ACTION";
+ case DhcpClient.CMD_ON_QUIT:
+ return "DhcpClient.CMD_ON_QUIT";
}
return "UNKNOWN:" + Integer.toString(what);
}
@@ -541,7 +541,7 @@
setLinkProperties(assembleLinkProperties());
break;
- case DhcpStateMachine.CMD_ON_QUIT:
+ case DhcpClient.CMD_ON_QUIT:
// Everything is already stopped.
Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped).");
break;
@@ -565,7 +565,7 @@
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
- case DhcpStateMachine.CMD_ON_QUIT:
+ case DhcpClient.CMD_ON_QUIT:
mDhcpStateMachine = null;
transitionTo(mStoppedState);
break;
@@ -617,7 +617,7 @@
// Start DHCPv4.
makeDhcpStateMachine();
mDhcpStateMachine.registerForPreDhcpNotification();
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
+ mDhcpStateMachine.sendMessage(DhcpClient.CMD_START_DHCP);
}
}
@@ -627,7 +627,7 @@
mIpReachabilityMonitor = null;
if (mDhcpStateMachine != null) {
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
+ mDhcpStateMachine.sendMessage(DhcpClient.CMD_STOP_DHCP);
mDhcpStateMachine.doQuit();
}
@@ -660,8 +660,7 @@
// calls completedPreDhcpAction() after provisioning with
// a static IP configuration.
if (mDhcpStateMachine != null) {
- mDhcpStateMachine.sendMessage(
- DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
+ mDhcpStateMachine.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
}
break;
@@ -678,12 +677,12 @@
break;
}
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+ case DhcpClient.CMD_PRE_DHCP_ACTION:
if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); }
mCallback.onPreDhcpAction();
break;
- case DhcpStateMachine.CMD_POST_DHCP_ACTION: {
+ case DhcpClient.CMD_POST_DHCP_ACTION: {
// Note that onPostDhcpAction() is likely to be
// asynchronous, and thus there is no guarantee that we
// will be able to observe any of its effects here.
@@ -692,10 +691,10 @@
final DhcpResults dhcpResults = (DhcpResults) msg.obj;
switch (msg.arg1) {
- case DhcpStateMachine.DHCP_SUCCESS:
+ case DhcpClient.DHCP_SUCCESS:
handleIPv4Success(dhcpResults);
break;
- case DhcpStateMachine.DHCP_FAILURE:
+ case DhcpClient.DHCP_FAILURE:
handleIPv4Failure();
break;
default:
@@ -704,7 +703,7 @@
break;
}
- case DhcpStateMachine.CMD_ON_QUIT:
+ case DhcpClient.CMD_ON_QUIT:
// DHCPv4 quit early for some reason.
Log.e(mTag, "Unexpected CMD_ON_QUIT.");
mDhcpStateMachine = null;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 64f60d93..467ecd7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -22,9 +22,12 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.wifi.WifiInfo;
import android.os.Build.VERSION_CODES;
+import android.os.Build;
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
@@ -995,6 +998,7 @@
public void testApplicationRestrictionsManagingApp() throws Exception {
setAsProfileOwner(admin1);
+ final String nonExistAppRestrictionsManagerPackage = "com.google.app.restrictions.manager2";
final String appRestrictionsManagerPackage = "com.google.app.restrictions.manager";
final int appRestrictionsManagerAppId = 20987;
final int appRestrictionsManagerUid = UserHandle.getUid(
@@ -1004,6 +1008,14 @@
eq(DpmMockContext.CALLER_USER_HANDLE));
mContext.binder.callingUid = appRestrictionsManagerUid;
+ final PackageInfo pi = new PackageInfo();
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.flags = ApplicationInfo.FLAG_HAS_CODE;
+ doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+ eq(appRestrictionsManagerPackage),
+ anyInt(),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+
// appRestrictionsManager package shouldn't be able to manage restrictions as the PO hasn't
// delegated that permission yet.
assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
@@ -1028,6 +1040,16 @@
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
+ // Check the API does not allow setting a non-existent package
+ try {
+ dpm.setApplicationRestrictionsManagingPackage(admin1,
+ nonExistAppRestrictionsManagerPackage);
+ fail("Non-existent app set as app restriction manager.");
+ } catch (IllegalArgumentException expected) {
+ MoreAsserts.assertContainsRegex(
+ "is not installed on the current user", expected.getMessage());
+ }
+
// Let appRestrictionsManagerPackage manage app restrictions
dpm.setApplicationRestrictionsManagingPackage(admin1, appRestrictionsManagerPackage);
assertEquals(appRestrictionsManagerPackage,
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index 23a69d1..69259d0 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -17,6 +17,7 @@
package com.android.ims.internal;
import com.android.ims.ImsReasonInfo;
+
/**
* A listener type for receiving notifications about the changes to
* the IMS connection(registration).
@@ -26,15 +27,36 @@
interface IImsRegistrationListener {
/**
* Notifies the application when the device is connected to the IMS network.
+ *
+ * @deprecated see {@link registrationConnectedWithRadioTech}
*/
void registrationConnected();
/**
* Notifies the application when the device is trying to connect the IMS network.
+ *
+ * @deprecated see {@link registrationProgressingWithRadioTech}
*/
void registrationProgressing();
/**
+ * Notifies the application when the device is connected to the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are {@code
+ * RIL_RADIO_TECHNOLOGY_*} defined in {@link ServiceState}.
+ */
+ void registrationConnectedWithRadioTech(int imsRadioTech);
+
+ /**
+ * Notifies the application when the device is trying to connect the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are {@code
+ * RIL_RADIO_TECHNOLOGY_*} defined in {@link ServiceState}.
+ */
+ void registrationProgressingWithRadioTech(int imsRadioTech);
+
+
+ /**
* Notifies the application when the device is disconnected from the IMS network.
*/
void registrationDisconnected(in ImsReasonInfo imsReasonInfo);
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 4d9ba6c..18a1943 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1883,8 +1883,6 @@
//printf("Comment of %s: %s\n", String8(e).string(),
// String8(cmt).string());
syms->appendComment(String8(e), String16(cmt), srcPos);
- } else {
- //printf("No comment for %s\n", String8(e).string());
}
syms->makeSymbolPublic(String8(e), srcPos);
} else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
@@ -2535,10 +2533,6 @@
fprintf(fp,
"%s/** %s\n",
getIndentSpace(indent), cmt.string());
- } else if (sym.isPublic && !includePrivate) {
- sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
- assets->getPackage().string(), className.string(),
- String8(sym.name).string());
}
String16 typeComment(sym.typeComment);
if (typeComment.size() > 0) {
@@ -2581,10 +2575,6 @@
"%s */\n",
getIndentSpace(indent), cmt.string(),
getIndentSpace(indent));
- } else if (sym.isPublic && !includePrivate) {
- sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
- assets->getPackage().string(), className.string(),
- String8(sym.name).string());
}
ann.printAnnotations(fp, getIndentSpace(indent));
fprintf(fp, "%spublic static final String %s=\"%s\";\n",
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index c7b24bc..b6588b6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -288,8 +288,10 @@
}
@LayoutlibDelegate
- /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, final String path,
+ /*package*/ static boolean nAddFontWeightStyle(long nativeFamily,
+ final String path, final int index, final List<FontListParser.Axis> axes,
final int weight, final boolean isItalic) {
+ // 'index' and 'axes' are not supported by java.awt.Font
final FontFamily_Delegate delegate = getDelegate(nativeFamily);
if (delegate != null) {
if (sFontLocation == null) {
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index d45ba03..4921073 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -44,6 +44,7 @@
import com.android.internal.util.Protocol;
import java.net.InetAddress;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -1319,13 +1320,15 @@
* @return the list of access points found in the most recent scan. An app must hold
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
- * in order to get valid results.
+ * in order to get valid results. If there is a remote exception (e.g., either a communication
+ * problem with the system service or an exception within the framework) an empty list will be
+ * returned.
*/
public List<ScanResult> getScanResults() {
try {
return mService.getScanResults(mContext.getOpPackageName());
} catch (RemoteException e) {
- return null;
+ return new ArrayList<ScanResult>();
}
}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
index 5c18bd7..9e6ed4e 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
@@ -36,7 +36,7 @@
*/
public class WifiNanEventListener {
private static final String TAG = "WifiNanEventListener";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
/**
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
index cb82268..667c4b1 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -38,7 +38,7 @@
*/
public class WifiNanManager {
private static final String TAG = "WifiNanManager";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
private IBinder mBinder;
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java
index d0a9410..bc1787f 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSession.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java
@@ -27,7 +27,7 @@
*/
public class WifiNanSession {
private static final String TAG = "WifiNanSession";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
/**
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
index 0925087..b9af7def 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
@@ -43,7 +43,7 @@
*/
public class WifiNanSessionListener {
private static final String TAG = "WifiNanSessionListener";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
/**