Merge "SensorManager API changes." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index b07907b..dba086c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5382,7 +5382,6 @@
method public android.os.UserHandle createUser(android.content.ComponentName, java.lang.String);
method public void enableSystemApp(android.content.ComponentName, java.lang.String);
method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
- method public void setTrustAgentFeaturesEnabled(android.content.ComponentName, android.content.ComponentName, java.util.List<java.lang.String>);
method public java.lang.String[] getAccountTypesWithManagementDisabled();
method public java.util.List<android.content.ComponentName> getActiveAdmins();
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
@@ -5454,6 +5453,7 @@
method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public int setStorageEncryption(android.content.ComponentName, boolean);
+ method public void setTrustAgentFeaturesEnabled(android.content.ComponentName, android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
method public void uninstallCaCert(android.content.ComponentName, byte[]);
method public void wipeData(int);
@@ -12669,7 +12669,7 @@
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_PHYSICAL_SIZE;
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_PIXEL_ARRAY_SIZE;
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_SENSITIVITY_RANGE;
- field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_TIMESTAMP_CALIBRATION;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_TIMESTAMP_SOURCE;
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_WHITE_LEVEL;
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_MAX_ANALOG_SENSITIVITY;
field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_ORIENTATION;
@@ -12863,8 +12863,8 @@
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG = 1; // 0x1
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB = 4; // 0x4
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB = 0; // 0x0
- field public static final int SENSOR_INFO_TIMESTAMP_CALIBRATION_CALIBRATED = 1; // 0x1
- field public static final int SENSOR_INFO_TIMESTAMP_CALIBRATION_UNCALIBRATED = 0; // 0x0
+ field public static final int SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME = 1; // 0x1
+ field public static final int SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN = 0; // 0x0
field public static final int SENSOR_REFERENCE_ILLUMINANT1_CLOUDY_WEATHER = 10; // 0xa
field public static final int SENSOR_REFERENCE_ILLUMINANT1_COOL_WHITE_FLUORESCENT = 14; // 0xe
field public static final int SENSOR_REFERENCE_ILLUMINANT1_D50 = 23; // 0x17
@@ -12992,7 +12992,7 @@
public class CaptureResult extends android.hardware.camera2.CameraMetadata {
method public T get(android.hardware.camera2.CaptureResult.Key<T>);
- method public int getFrameNumber();
+ method public long getFrameNumber();
method public android.hardware.camera2.CaptureRequest getRequest();
method public int getSequenceId();
field public static final android.hardware.camera2.CaptureResult.Key BLACK_LEVEL_LOCK;
@@ -13038,7 +13038,6 @@
field public static final android.hardware.camera2.CaptureResult.Key LENS_OPTICAL_STABILIZATION_MODE;
field public static final android.hardware.camera2.CaptureResult.Key LENS_STATE;
field public static final android.hardware.camera2.CaptureResult.Key NOISE_REDUCTION_MODE;
- field public static final android.hardware.camera2.CaptureResult.Key REQUEST_FRAME_COUNT;
field public static final android.hardware.camera2.CaptureResult.Key REQUEST_PIPELINE_DEPTH;
field public static final android.hardware.camera2.CaptureResult.Key SCALER_CROP_REGION;
field public static final android.hardware.camera2.CaptureResult.Key SENSOR_EXPOSURE_TIME;
@@ -13175,7 +13174,6 @@
method public boolean isOutputSupportedFor(int);
method public static boolean isOutputSupportedFor(java.lang.Class<T>);
method public boolean isOutputSupportedFor(android.view.Surface);
- field public static final long NO_MIN_FRAME_DURATION = 0L; // 0x0L
}
public final class TonemapCurve {
@@ -28559,9 +28557,11 @@
}
public final class CallCameraCapabilities implements android.os.Parcelable {
- ctor public CallCameraCapabilities(boolean, float);
+ ctor public CallCameraCapabilities(boolean, float, int, int);
method public int describeContents();
+ method public int getHeight();
method public float getMaxZoom();
+ method public int getWidth();
method public boolean isZoomSupported();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -28692,15 +28692,15 @@
public abstract class ConnectionService extends android.app.Service {
ctor public ConnectionService();
- method public final android.telecomm.RemoteConnection createRemoteIncomingConnection(android.telecomm.ConnectionRequest);
- method public final android.telecomm.RemoteConnection createRemoteOutgoingConnection(android.telecomm.ConnectionRequest);
+ method public final android.telecomm.RemoteConnection createRemoteIncomingConnection(android.telecomm.PhoneAccountHandle, android.telecomm.ConnectionRequest);
+ method public final android.telecomm.RemoteConnection createRemoteOutgoingConnection(android.telecomm.PhoneAccountHandle, android.telecomm.ConnectionRequest);
method public final java.util.Collection<android.telecomm.Connection> getAllConnections();
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnectionAdded(android.telecomm.Connection);
method public void onConnectionRemoved(android.telecomm.Connection);
method public void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
- method public android.telecomm.Connection onCreateIncomingConnection(android.telecomm.ConnectionRequest);
- method public android.telecomm.Connection onCreateOutgoingConnection(android.telecomm.ConnectionRequest);
+ method public android.telecomm.Connection onCreateIncomingConnection(android.telecomm.PhoneAccountHandle, android.telecomm.ConnectionRequest);
+ method public android.telecomm.Connection onCreateOutgoingConnection(android.telecomm.PhoneAccountHandle, android.telecomm.ConnectionRequest);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecomm.ConnectionService";
}
@@ -28818,7 +28818,7 @@
}
public class PhoneAccount implements android.os.Parcelable {
- ctor public PhoneAccount(android.telecomm.PhoneAccountHandle, android.net.Uri, java.lang.String, int, int, java.lang.CharSequence, java.lang.CharSequence, boolean);
+ ctor public PhoneAccount(android.telecomm.PhoneAccountHandle, android.net.Uri, java.lang.String, int, int, java.lang.CharSequence, java.lang.CharSequence);
method public int describeContents();
method public android.telecomm.PhoneAccountHandle getAccountHandle();
method public int getCapabilities();
@@ -28828,10 +28828,10 @@
method public java.lang.CharSequence getLabel();
method public java.lang.CharSequence getShortDescription();
method public java.lang.String getSubscriptionNumber();
- method public boolean isVideoCallingSupported();
method public void writeToParcel(android.os.Parcel, int);
- field public static final int CAPABILITY_SIM_CALL_MANAGER = 1; // 0x1
+ field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+ field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final android.os.Parcelable.Creator CREATOR;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index de5b9c4..3a764fa 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5081,7 +5081,12 @@
}
public static ActivityThread systemMain() {
- HardwareRenderer.disable(true);
+ // The system process on low-memory devices do not get to use hardware
+ // accelerated drawing, since this can add too much overhead to the
+ // process.
+ if (!ActivityManager.isHighEndGfx()) {
+ HardwareRenderer.disable(true);
+ }
ActivityThread thread = new ActivityThread();
thread.attach(true);
return thread;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0e6f86e..9310bf8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -361,6 +361,40 @@
= "android.app.action.ADD_DEVICE_ADMIN";
/**
+ * @hide
+ * Activity action: ask the user to add a new device administrator as the profile owner
+ * for this user. Only system privileged apps that have MANAGE_USERS and MANAGE_DEVICE_ADMINS
+ * permission can call this API.
+ *
+ * <p>The ComponentName of the profile owner admin is pass in {@link #EXTRA_DEVICE_ADMIN} extra
+ * field. This will invoke a UI to bring the user through adding the profile owner admin
+ * to remotely control restrictions on the user.
+ *
+ * <p>The intent must be invoked via {@link Activity#startActivityForResult()} to receive the
+ * result of whether or not the user approved the action. If approved, the result will
+ * be {@link Activity#RESULT_OK} and the component will be set as an active admin as well
+ * as a profile owner.
+ *
+ * <p>You can optionally include the {@link #EXTRA_ADD_EXPLANATION}
+ * field to provide the user with additional explanation (in addition
+ * to your component's description) about what is being added.
+ *
+ * <p>If there is already a profile owner active or the caller doesn't have the required
+ * permissions, the operation will return a failure result.
+ */
+ @SystemApi
+ public static final String ACTION_SET_PROFILE_OWNER
+ = "android.app.action.SET_PROFILE_OWNER";
+
+ /**
+ * @hide
+ * Name of the profile owner admin that controls the user.
+ */
+ @SystemApi
+ public static final String EXTRA_PROFILE_OWNER_NAME
+ = "android.app.extra.PROFILE_OWNER_NAME";
+
+ /**
* Activity action: send when any policy admin changes a policy.
* This is generally used to find out when a new policy is in effect.
*
@@ -397,6 +431,7 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SET_NEW_PASSWORD
= "android.app.action.SET_NEW_PASSWORD";
+
/**
* Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from a
* managed profile to its parent.
@@ -2018,7 +2053,6 @@
return false;
}
-
/**
* Used to determine if a particular package has been registered as a Device Owner app.
* A device owner app is a special device admin that cannot be deactivated by the user, once
@@ -2096,6 +2130,7 @@
/**
* @hide
+ * @deprecated Use #ACTION_SET_PROFILE_OWNER
* Sets the given component as an active admin and registers the package as the profile
* owner for this user. The package must already be installed and there shouldn't be
* an existing profile owner registered for this user. Also, this method must be called
@@ -2116,7 +2151,7 @@
try {
final int myUserId = UserHandle.myUserId();
mService.setActiveAdmin(admin, false, myUserId);
- return mService.setProfileOwner(admin.getPackageName(), ownerName, myUserId);
+ return mService.setProfileOwner(admin, ownerName, myUserId);
} catch (RemoteException re) {
Log.w(TAG, "Failed to set profile owner " + re);
throw new IllegalArgumentException("Couldn't set profile owner.", re);
@@ -2127,6 +2162,42 @@
/**
* @hide
+ * Clears the active profile owner and removes all user restrictions. The caller must
+ * be from the same package as the active profile owner for this user, otherwise a
+ * SecurityException will be thrown.
+ *
+ * @param admin The component to remove as the profile owner.
+ * @return
+ */
+ @SystemApi
+ public void clearProfileOwner(ComponentName admin) {
+ if (mService != null) {
+ try {
+ mService.clearProfileOwner(admin);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed to clear profile owner " + admin + re);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Checks if the user was already setup.
+ */
+ public boolean hasUserSetupCompleted() {
+ if (mService != null) {
+ try {
+ return mService.hasUserSetupCompleted();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed to check if user setup has completed");
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @deprecated Use setProfileOwner(ComponentName ...)
+ * @hide
* Sets the given package as the profile owner of the given user profile. The package must
* already be installed and there shouldn't be an existing profile owner registered for this
* user. Also, this method must be called before the user has been used for the first time.
@@ -2139,9 +2210,35 @@
*/
public boolean setProfileOwner(String packageName, String ownerName, int userHandle)
throws IllegalArgumentException {
+ if (packageName == null) {
+ throw new NullPointerException("packageName cannot be null");
+ }
+ return setProfileOwner(new ComponentName(packageName, ""), ownerName, userHandle);
+ }
+
+ /**
+ * @hide
+ * Sets the given component as the profile owner of the given user profile. The package must
+ * already be installed and there shouldn't be an existing profile owner registered for this
+ * user. Only the system can call this API if the user has already completed setup.
+ * @param admin the component name to be registered as profile owner.
+ * @param ownerName the human readable name of the organisation associated with this DPM.
+ * @param userHandle the userId to set the profile owner for.
+ * @return whether the component was successfully registered as the profile owner.
+ * @throws IllegalArgumentException if admin is null, the package isn't installed, or
+ * the user has already been set up.
+ */
+ public boolean setProfileOwner(ComponentName admin, String ownerName, int userHandle)
+ throws IllegalArgumentException {
+ if (admin == null) {
+ throw new NullPointerException("admin cannot be null");
+ }
if (mService != null) {
try {
- return mService.setProfileOwner(packageName, ownerName, userHandle);
+ if (ownerName == null) {
+ ownerName = "";
+ }
+ return mService.setProfileOwner(admin, ownerName, userHandle);
} catch (RemoteException re) {
Log.w(TAG, "Failed to set profile owner", re);
throw new IllegalArgumentException("Couldn't set profile owner.", re);
@@ -2200,7 +2297,7 @@
if (mService != null) {
try {
String profileOwnerPackage = mService.getProfileOwner(
- Process.myUserHandle().getIdentifier());
+ Process.myUserHandle().getIdentifier()).getPackageName();
return profileOwnerPackage != null && profileOwnerPackage.equals(packageName);
} catch (RemoteException re) {
Log.w(TAG, "Failed to check profile owner");
@@ -2215,7 +2312,7 @@
* owner has been set for that user.
* @throws IllegalArgumentException if the userId is invalid.
*/
- public String getProfileOwner() throws IllegalArgumentException {
+ public ComponentName getProfileOwner() throws IllegalArgumentException {
if (mService != null) {
try {
return mService.getProfileOwner(Process.myUserHandle().getIdentifier());
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index a6544e6..6ce737a 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -113,11 +113,13 @@
String getDeviceOwnerName();
void clearDeviceOwner(String packageName);
- boolean setProfileOwner(String packageName, String ownerName, int userHandle);
- String getProfileOwner(int userHandle);
+ boolean setProfileOwner(in ComponentName who, String ownerName, int userHandle);
+ ComponentName getProfileOwner(int userHandle);
String getProfileOwnerName(int userHandle);
void setProfileEnabled(in ComponentName who);
void setProfileName(in ComponentName who, String profileName);
+ void clearProfileOwner(in ComponentName who);
+ boolean hasUserSetupCompleted();
boolean installCaCert(in ComponentName admin, in byte[] certBuffer);
void uninstallCaCert(in ComponentName admin, in String alias);
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 2ce18b0..30aaf2e 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -110,21 +110,27 @@
dest.writeInt(mServiceDataUuid == null ? 0 : 1);
if (mServiceDataUuid != null) {
dest.writeParcelable(mServiceDataUuid, flags);
- dest.writeInt(mServiceData == null ? 0 : mServiceData.length);
+ dest.writeInt(mServiceData == null ? 0 : 1);
if (mServiceData != null) {
+ dest.writeInt(mServiceData.length);
dest.writeByteArray(mServiceData);
- dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length);
+
+ dest.writeInt(mServiceDataMask == null ? 0 : 1);
if (mServiceDataMask != null) {
+ dest.writeInt(mServiceDataMask.length);
dest.writeByteArray(mServiceDataMask);
}
}
}
dest.writeInt(mManufacturerId);
- dest.writeInt(mManufacturerData == null ? 0 : mManufacturerData.length);
+ dest.writeInt(mManufacturerData == null ? 0 : 1);
if (mManufacturerData != null) {
+ dest.writeInt(mManufacturerData.length);
dest.writeByteArray(mManufacturerData);
- dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length);
+
+ dest.writeInt(mManufacturerDataMask == null ? 0 : 1);
if (mManufacturerDataMask != null) {
+ dest.writeInt(mManufacturerDataMask.length);
dest.writeByteArray(mManufacturerDataMask);
}
}
@@ -164,29 +170,31 @@
if (in.readInt() == 1) {
ParcelUuid servcieDataUuid =
in.readParcelable(ParcelUuid.class.getClassLoader());
- int serviceDataLength = in.readInt();
- if (serviceDataLength > 0) {
+ if (in.readInt() == 1) {
+ int serviceDataLength = in.readInt();
byte[] serviceData = new byte[serviceDataLength];
in.readByteArray(serviceData);
- builder.setServiceData(servcieDataUuid, serviceData);
- int serviceDataMaskLength = in.readInt();
- if (serviceDataMaskLength > 0) {
+ if (in.readInt() == 0) {
+ builder.setServiceData(servcieDataUuid, serviceData);
+ } else {
+ int serviceDataMaskLength = in.readInt();
byte[] serviceDataMask = new byte[serviceDataMaskLength];
in.readByteArray(serviceDataMask);
builder.setServiceData(
- servcieDataUuid, serviceData, serviceDataMask);
+ servcieDataUuid, serviceData, serviceDataMask);
}
}
}
int manufacturerId = in.readInt();
- int manufacturerDataLength = in.readInt();
- if (manufacturerDataLength > 0) {
+ if (in.readInt() == 1) {
+ int manufacturerDataLength = in.readInt();
byte[] manufacturerData = new byte[manufacturerDataLength];
in.readByteArray(manufacturerData);
- builder.setManufacturerData(manufacturerId, manufacturerData);
- int manufacturerDataMaskLength = in.readInt();
- if (manufacturerDataMaskLength > 0) {
+ if (in.readInt() == 0) {
+ builder.setManufacturerData(manufacturerId, manufacturerData);
+ } else {
+ int manufacturerDataMaskLength = in.readInt();
byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
in.readByteArray(manufacturerDataMask);
builder.setManufacturerData(manufacturerId, manufacturerData,
@@ -349,12 +357,17 @@
// Check whether the data pattern matches the parsed data.
private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) {
- if (dataMask == null) {
- return Arrays.equals(data, parsedData);
- }
- if (parsedData == null) {
+ if (parsedData == null || parsedData.length < data.length) {
return false;
}
+ if (dataMask == null) {
+ for (int i = 0; i < data.length; ++i) {
+ if (parsedData[i] != data[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
for (int i = 0; i < data.length; ++i) {
if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) {
return false;
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 2d50b5a..c95a5bf 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -190,7 +190,7 @@
return info;
}
} catch (RemoteException re) {
- return null;
+ throw new RuntimeException("Failed to call LauncherAppsService");
}
return null;
}
@@ -259,7 +259,7 @@
try {
return mService.isPackageEnabled(packageName, user);
} catch (RemoteException re) {
- return false;
+ throw new RuntimeException("Failed to call LauncherAppsService");
}
}
@@ -275,7 +275,7 @@
try {
return mService.isActivityEnabled(component, user);
} catch (RemoteException re) {
- return false;
+ throw new RuntimeException("Failed to call LauncherAppsService");
}
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 6cb6a24..4e7f9dd 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1551,17 +1551,17 @@
new Key<Integer>("android.sensor.info.whiteLevel", int.class);
/**
- * <p>The sensor timestamp calibration quality.</p>
- * <p>The sensor timestamp calibration quality determines the reliability of
- * {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} provided by the camera device.</p>
- *
- * @see CaptureResult#SENSOR_TIMESTAMP
- * @see #SENSOR_INFO_TIMESTAMP_CALIBRATION_UNCALIBRATED
- * @see #SENSOR_INFO_TIMESTAMP_CALIBRATION_CALIBRATED
+ * <p>The time base source for sensor capture start timestamps.</p>
+ * <p>The timestamps provided for captures are always in nanoseconds and monotonic, but
+ * may not based on a time source that can be compared to other system time sources.</p>
+ * <p>This characteristic defines the source for the timestamps, and therefore whether they
+ * can be compared against other system time sources/timestamps.</p>
+ * @see #SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN
+ * @see #SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME
*/
@PublicKey
- public static final Key<Integer> SENSOR_INFO_TIMESTAMP_CALIBRATION =
- new Key<Integer>("android.sensor.info.timestampCalibration", int.class);
+ public static final Key<Integer> SENSOR_INFO_TIMESTAMP_SOURCE =
+ new Key<Integer>("android.sensor.info.timestampSource", int.class);
/**
* <p>The standard reference illuminant used as the scene light source when
@@ -1934,7 +1934,7 @@
* android.sync.frameNumber to a non-negative value).</p>
* <p>This defines the maximum distance (in number of metadata results),
* between android.sync.frameNumber and the equivalent
- * android.request.frameCount.</p>
+ * frame number for that result.</p>
* <p>In other words this acts as an upper boundary for how many frames
* must occur before the camera device knows for a fact that the new
* submitted camera settings have been applied in outgoing frames.</p>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index ebbfc63..fbca395 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -494,7 +494,7 @@
public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB = 4;
//
- // Enumeration values for CameraCharacteristics#SENSOR_INFO_TIMESTAMP_CALIBRATION
+ // Enumeration values for CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
//
/**
@@ -506,9 +506,9 @@
* and the result metadata generated by a single capture are identical.</p>
*
* @see CaptureResult#SENSOR_TIMESTAMP
- * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_CALIBRATION
+ * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
*/
- public static final int SENSOR_INFO_TIMESTAMP_CALIBRATION_UNCALIBRATED = 0;
+ public static final int SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN = 0;
/**
* <p>Timestamps from {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} are in the same timebase as
@@ -516,9 +516,9 @@
* and they can be compared to other timestamps using that base.</p>
*
* @see CaptureResult#SENSOR_TIMESTAMP
- * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_CALIBRATION
+ * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
*/
- public static final int SENSOR_INFO_TIMESTAMP_CALIBRATION_CALIBRATED = 1;
+ public static final int SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME = 1;
//
// Enumeration values for CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
@@ -664,7 +664,7 @@
/**
* <p>Every frame has the requests immediately applied.</p>
* <p>Furthermore for all results,
- * <code>android.sync.frameNumber == android.request.frameCount</code></p>
+ * <code>android.sync.frameNumber == CaptureResult#getFrameNumber()</code></p>
* <p>Changing controls over multiple requests one after another will
* produce results that have those controls applied atomically
* each frame.</p>
@@ -679,6 +679,7 @@
* <p>By submitting a series of identical requests, the camera device
* will eventually have the camera settings applied, but it is
* unknown when that exact point will be.</p>
+ * <p>All LEGACY capability devices will have this as their maxLatency.</p>
* @see CameraCharacteristics#SYNC_MAX_LATENCY
*/
public static final int SYNC_MAX_LATENCY_UNKNOWN = -1;
@@ -1207,8 +1208,7 @@
* image while recording video) use case.</p>
* <p>The camera device should take the highest-quality image
* possible (given the other settings) without disrupting the
- * frame rate of video recording.<br />
- * </p>
+ * frame rate of video recording. </p>
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
*/
public static final int CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT = 4;
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 20a04f0..63dff55 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -17,6 +17,7 @@
package android.hardware.camera2;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.CaptureResultExtras;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.utils.TypeReference;
@@ -142,12 +143,16 @@
private final CameraMetadataNative mResults;
private final CaptureRequest mRequest;
private final int mSequenceId;
+ private final long mFrameNumber;
/**
* Takes ownership of the passed-in properties object
+ *
+ * <p>For internal use only</p>
* @hide
*/
- public CaptureResult(CameraMetadataNative results, CaptureRequest parent, int sequenceId) {
+ public CaptureResult(CameraMetadataNative results, CaptureRequest parent,
+ CaptureResultExtras extras) {
if (results == null) {
throw new IllegalArgumentException("results was null");
}
@@ -156,12 +161,17 @@
throw new IllegalArgumentException("parent was null");
}
+ if (extras == null) {
+ throw new IllegalArgumentException("extras was null");
+ }
+
mResults = CameraMetadataNative.move(results);
if (mResults.isEmpty()) {
throw new AssertionError("Results must not be empty");
}
mRequest = parent;
- mSequenceId = sequenceId;
+ mSequenceId = extras.getRequestId();
+ mFrameNumber = extras.getFrameNumber();
}
/**
@@ -190,6 +200,7 @@
mRequest = null;
mSequenceId = sequenceId;
+ mFrameNumber = -1;
}
/**
@@ -288,11 +299,10 @@
* for every new result or failure; and the scope is the lifetime of the
* {@link CameraDevice}.</p>
*
- * @return int frame number
+ * @return The frame number
*/
- public int getFrameNumber() {
- // TODO: @hide REQUEST_FRAME_COUNT
- return get(REQUEST_FRAME_COUNT);
+ public long getFrameNumber() {
+ return mFrameNumber;
}
/**
@@ -2026,8 +2036,10 @@
* increases with every new result (that is, each new result has a unique
* frameCount value).</p>
* <p>Reset on release()</p>
+ * @deprecated
+ * @hide
*/
- @PublicKey
+ @Deprecated
public static final Key<Integer> REQUEST_FRAME_COUNT =
new Key<Integer>("android.request.frameCount", int.class);
@@ -2209,18 +2221,18 @@
* <p>The timestamps are also included in all image
* buffers produced for the same capture, and will be identical
* on all the outputs.</p>
- * <p>When {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_CALIBRATION android.sensor.info.timestampCalibration} <code>==</code> UNCALIBRATED,
+ * <p>When {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE android.sensor.info.timestampSource} <code>==</code> UNKNOWN,
* the timestamps measure time since an unspecified starting point,
* and are monotonically increasing. They can be compared with the
* timestamps for other captures from the same camera device, but are
* not guaranteed to be comparable to any other time source.</p>
- * <p>When {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_CALIBRATION android.sensor.info.timestampCalibration} <code>==</code> CALIBRATED,
+ * <p>When {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE android.sensor.info.timestampSource} <code>==</code> REALTIME,
* the timestamps measure time in the same timebase as
* android.os.SystemClock#elapsedRealtimeNanos(), and they can be
* compared to other timestamps from other subsystems that are using
* that base.</p>
*
- * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_CALIBRATION
+ * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
*/
@PublicKey
public static final Key<Long> SENSOR_TIMESTAMP =
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index 226f09d..ec4bc7d 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -17,6 +17,7 @@
package android.hardware.camera2;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.CaptureResultExtras;
import java.util.Collections;
import java.util.List;
@@ -51,8 +52,9 @@
* Takes ownership of the passed-in properties object
* @hide
*/
- public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent, int sequenceId) {
- super(results, parent, sequenceId);
+ public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
+ CaptureResultExtras extras) {
+ super(results, parent, extras);
}
/**
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ed4e457..18b1202 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -313,6 +313,7 @@
return mCameraId;
}
+ @Override
public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
// Treat a null input the same an empty list
if (outputs == null) {
@@ -448,6 +449,7 @@
}
}
+ @Override
public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
throws CameraAccessException {
if (DEBUG) {
@@ -458,6 +460,7 @@
return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
}
+ @Override
public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Handler handler) throws CameraAccessException {
if (requests == null || requests.isEmpty()) {
@@ -610,6 +613,7 @@
}
}
+ @Override
public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Handler handler) throws CameraAccessException {
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
@@ -617,6 +621,7 @@
return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
}
+ @Override
public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Handler handler) throws CameraAccessException {
if (requests == null || requests.isEmpty()) {
@@ -625,6 +630,7 @@
return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
}
+ @Override
public void stopRepeating() throws CameraAccessException {
synchronized(mInterfaceLock) {
@@ -675,6 +681,7 @@
}
}
+ @Override
public void flush() throws CameraAccessException {
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
@@ -1031,8 +1038,10 @@
CaptureResultExtras resultExtras) throws RemoteException {
int requestId = resultExtras.getRequestId();
+ long frameNumber = resultExtras.getFrameNumber();
+
if (DEBUG) {
- Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id "
+ Log.v(TAG, "Received result frame " + frameNumber + " for id "
+ requestId);
}
@@ -1051,7 +1060,7 @@
// Update tracker (increment counter) when it's not a partial result.
if (!isPartialResult) {
- mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(),
+ mFrameNumberTracker.updateTracker(frameNumber,
/*error*/false);
}
@@ -1060,7 +1069,7 @@
if (DEBUG) {
Log.d(TAG,
"holder is null, early return at frame "
- + resultExtras.getFrameNumber());
+ + frameNumber);
}
return;
}
@@ -1069,7 +1078,7 @@
if (DEBUG) {
Log.d(TAG,
"camera is closed, early return at frame "
- + resultExtras.getFrameNumber());
+ + frameNumber);
}
return;
}
@@ -1082,7 +1091,7 @@
// Either send a partial result or the final capture completed result
if (isPartialResult) {
final CaptureResult resultAsCapture =
- new CaptureResult(result, request, requestId);
+ new CaptureResult(result, request, resultExtras);
// Partial result
resultDispatch = new Runnable() {
@@ -1098,7 +1107,7 @@
};
} else {
final TotalCaptureResult resultAsCapture =
- new TotalCaptureResult(result, request, requestId);
+ new TotalCaptureResult(result, request, resultExtras);
// Final capture result
resultDispatch = new Runnable() {
diff --git a/core/java/android/hardware/camera2/legacy/CaptureCollector.java b/core/java/android/hardware/camera2/legacy/CaptureCollector.java
index ab31d8c..af58a8a 100644
--- a/core/java/android/hardware/camera2/legacy/CaptureCollector.java
+++ b/core/java/android/hardware/camera2/legacy/CaptureCollector.java
@@ -15,11 +15,12 @@
*/
package android.hardware.camera2.legacy;
-import android.hardware.camera2.impl.CameraMetadataNative;
import android.util.Log;
+import android.util.MutableLong;
import android.util.Pair;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@@ -77,7 +78,7 @@
if (needsPreview && isPreviewCompleted()) {
CaptureCollector.this.onPreviewCompleted();
}
- CaptureCollector.this.onRequestCompleted(mRequest, mLegacy, mTimestamp);
+ CaptureCollector.this.onRequestCompleted(this);
}
}
@@ -176,13 +177,13 @@
private final ArrayDeque<CaptureHolder> mJpegProduceQueue;
private final ArrayDeque<CaptureHolder> mPreviewCaptureQueue;
private final ArrayDeque<CaptureHolder> mPreviewProduceQueue;
+ private final ArrayList<CaptureHolder> mCompletedRequests = new ArrayList<>();
private final ReentrantLock mLock = new ReentrantLock();
private final Condition mIsEmpty;
private final Condition mPreviewsEmpty;
private final Condition mNotFull;
private final CameraDeviceState mDeviceState;
- private final LegacyResultMapper mMapper = new LegacyResultMapper();
private int mInFlight = 0;
private int mInFlightPreviews = 0;
private final int mMaxInFlight;
@@ -320,6 +321,54 @@
}
/**
+ * Wait for the specified request to be completed (all buffers available).
+ *
+ * <p>May not wait for the same request more than once, since a successful wait
+ * will erase the history of that request.</p>
+ *
+ * @param holder the {@link RequestHolder} for this request.
+ * @param timeout a timeout to use for this call.
+ * @param unit the units to use for the timeout.
+ * @param timestamp the timestamp of the request will be written out to here, in ns
+ *
+ * @return {@code false} if this method timed out.
+ *
+ * @throws InterruptedException if this thread is interrupted.
+ */
+ public boolean waitForRequestCompleted(RequestHolder holder, long timeout, TimeUnit unit,
+ MutableLong timestamp)
+ throws InterruptedException {
+ long nanos = unit.toNanos(timeout);
+ final ReentrantLock lock = this.mLock;
+ lock.lock();
+ try {
+ while (!removeRequestIfCompleted(holder, /*out*/timestamp)) {
+ if (nanos <= 0) {
+ return false;
+ }
+ nanos = mNotFull.awaitNanos(nanos);
+ }
+ return true;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ private boolean removeRequestIfCompleted(RequestHolder holder, MutableLong timestamp) {
+ int i = 0;
+ for (CaptureHolder h : mCompletedRequests) {
+ if (h.mRequest.equals(holder)) {
+ timestamp.value = h.mTimestamp;
+ mCompletedRequests.remove(i);
+ return true;
+ }
+ i++;
+ }
+
+ return false;
+ }
+
+ /**
* Called to alert the {@link CaptureCollector} that the jpeg capture has begun.
*
* @param timestamp the time of the jpeg capture.
@@ -431,8 +480,9 @@
}
}
- private void onRequestCompleted(RequestHolder request, LegacyRequest legacyHolder,
- long timestamp) {
+ private void onRequestCompleted(CaptureHolder capture) {
+ RequestHolder request = capture.mRequest;
+
mInFlight--;
if (DEBUG) {
Log.d(TAG, "Completed request " + request.getRequestId() +
@@ -442,12 +492,12 @@
throw new IllegalStateException(
"More captures completed than requests queued.");
}
+
+ mCompletedRequests.add(capture);
+
mNotFull.signalAll();
if (mInFlight == 0) {
mIsEmpty.signalAll();
}
- CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
- legacyHolder, timestamp);
- mDeviceState.setCaptureResult(request, result);
}
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java
new file mode 100644
index 0000000..e576b43
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.legacy;
+
+import android.hardware.Camera;
+import android.hardware.Camera.Parameters;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.utils.ParamsUtils;
+import android.util.Log;
+
+import java.util.Objects;
+
+import static android.hardware.camera2.CaptureRequest.*;
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Map capture request data into legacy focus state transitions.
+ *
+ * <p>This object will asynchronously process auto-focus changes, so no interaction
+ * with it is necessary beyond reading the current state and updating with the latest trigger.</p>
+ */
+@SuppressWarnings("deprecation")
+public class LegacyFocusStateMapper {
+ private static String TAG = "LegacyFocusStateMapper";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private final Camera mCamera;
+
+ private int mAfStatePrevious = CONTROL_AF_STATE_INACTIVE;
+ private String mAfModePrevious = null;
+
+ /** Guard mAfRun and mAfState */
+ private final Object mLock = new Object();
+ /** Guard access with mLock */
+ private int mAfRun = 0;
+ /** Guard access with mLock */
+ private int mAfState = CONTROL_AF_STATE_INACTIVE;
+
+ /**
+ * Instantiate a new focus state mapper.
+ *
+ * @param camera a non-{@code null} camera1 device
+ *
+ * @throws NullPointerException if any of the args were {@code null}
+ */
+ public LegacyFocusStateMapper(Camera camera) {
+ mCamera = checkNotNull(camera, "camera must not be null");
+ }
+
+ /**
+ * Process the AF triggers from the request as a camera1 autofocus routine.
+ *
+ * <p>This method should be called after the parameters are {@link LegacyRequestMapper mapped}
+ * with the request.</p>
+ *
+ * <p>Callbacks are processed in the background, and the next call to {@link #mapResultTriggers}
+ * will have the latest AF state as reflected by the camera1 callbacks.</p>
+ *
+ * <p>None of the arguments will be mutated.</p>
+ *
+ * @param captureRequest a non-{@code null} request
+ * @param parameters a non-{@code null} parameters corresponding to this request (read-only)
+ */
+ public void processRequestTriggers(CaptureRequest captureRequest,
+ Camera.Parameters parameters) {
+ checkNotNull(captureRequest, "captureRequest must not be null");
+
+ /*
+ * control.afTrigger
+ */
+ int afTrigger = ParamsUtils.getOrDefault(captureRequest, CONTROL_AF_TRIGGER,
+ CONTROL_AF_TRIGGER_IDLE);
+
+ final String afMode = parameters.getFocusMode();
+
+ if (!Objects.equals(mAfModePrevious, afMode)) {
+ if (VERBOSE) {
+ Log.v(TAG, "processRequestTriggers - AF mode switched from " + mAfModePrevious +
+ " to " + afMode);
+ }
+
+ // Switching modes always goes back to INACTIVE; ignore callbacks from previous modes
+
+ synchronized (mLock) {
+ ++mAfRun;
+ mAfState = CONTROL_AF_STATE_INACTIVE;
+ }
+ mCamera.cancelAutoFocus();
+ }
+
+ mAfModePrevious = afMode;
+
+ // Passive AF Scanning
+ {
+ final int currentAfRun;
+
+ synchronized (mLock) {
+ currentAfRun = mAfRun;
+ }
+
+ mCamera.setAutoFocusMoveCallback(new Camera.AutoFocusMoveCallback() {
+ @Override
+ public void onAutoFocusMoving(boolean start, Camera camera) {
+ synchronized (mLock) {
+ int latestAfRun = mAfRun;
+
+ if (VERBOSE) {
+ Log.v(TAG, "onAutoFocusMoving - start " + start + " latest AF run " +
+ latestAfRun + ", last AF run " + currentAfRun);
+ }
+
+ if (currentAfRun != latestAfRun) {
+ Log.d(TAG,
+ "onAutoFocusMoving - ignoring move callbacks from old af run"
+ + currentAfRun);
+ return;
+ }
+
+ int newAfState = start ?
+ CONTROL_AF_STATE_PASSIVE_SCAN :
+ CONTROL_AF_STATE_PASSIVE_FOCUSED;
+ // We never send CONTROL_AF_STATE_PASSIVE_UNFOCUSED
+
+ switch (afMode) {
+ case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
+ case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
+ break;
+ // This callback should never be sent in any other AF mode
+ default:
+ Log.w(TAG, "onAutoFocus - got unexpected onAutoFocus in mode "
+ + afMode);
+
+ }
+
+ mAfState = newAfState;
+ }
+ }
+ });
+ }
+
+ // AF Locking
+ switch (afTrigger) {
+ case CONTROL_AF_TRIGGER_START:
+
+ int afStateAfterStart;
+ switch (afMode) {
+ case Parameters.FOCUS_MODE_AUTO:
+ case Parameters.FOCUS_MODE_MACRO:
+ afStateAfterStart = CONTROL_AF_STATE_ACTIVE_SCAN;
+ break;
+ case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
+ case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
+ afStateAfterStart = CONTROL_AF_STATE_PASSIVE_SCAN;
+ default:
+ // EDOF, INFINITY
+ afStateAfterStart = CONTROL_AF_STATE_INACTIVE;
+ }
+
+ final int currentAfRun;
+ synchronized (mLock) {
+ currentAfRun = ++mAfRun;
+ mAfState = afStateAfterStart;
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "processRequestTriggers - got AF_TRIGGER_START, " +
+ "new AF run is " + currentAfRun);
+ }
+
+ mCamera.autoFocus(new Camera.AutoFocusCallback() {
+ @Override
+ public void onAutoFocus(boolean success, Camera camera) {
+ synchronized (mLock) {
+ int latestAfRun = mAfRun;
+
+ if (VERBOSE) {
+ Log.v(TAG, "onAutoFocus - success " + success + " latest AF run " +
+ latestAfRun + ", last AF run " + currentAfRun);
+ }
+
+ // Ignore old auto-focus results, since another trigger was requested
+ if (latestAfRun != currentAfRun) {
+ Log.d(TAG, String.format("onAutoFocus - ignoring AF callback " +
+ "(old run %d, new run %d)", currentAfRun, latestAfRun));
+
+ return;
+ }
+
+ int newAfState = success ?
+ CONTROL_AF_STATE_FOCUSED_LOCKED :
+ CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
+
+ switch (afMode) {
+ case Parameters.FOCUS_MODE_AUTO:
+ case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
+ case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
+ case Parameters.FOCUS_MODE_MACRO:
+ break;
+ // This callback should never be sent in any other AF mode
+ default:
+ Log.w(TAG, "onAutoFocus - got unexpected onAutoFocus in mode "
+ + afMode);
+
+ }
+
+ mAfState = newAfState;
+ }
+ }
+ });
+
+ break;
+ case CONTROL_AF_TRIGGER_CANCEL:
+ synchronized (mLock) {
+ int updatedAfRun;
+
+ synchronized (mLock) {
+ updatedAfRun = ++mAfRun;
+ mAfState = CONTROL_AF_STATE_INACTIVE;
+ }
+
+ mCamera.cancelAutoFocus();
+
+ if (VERBOSE) {
+ Log.v(TAG, "processRequestTriggers - got AF_TRIGGER_CANCEL, " +
+ "new AF run is " + updatedAfRun);
+ }
+ }
+
+ break;
+ case CONTROL_AF_TRIGGER_IDLE:
+ // No action necessary. The callbacks will handle transitions.
+ break;
+ default:
+ Log.w(TAG, "mapTriggers - ignoring unknown control.afTrigger = " + afTrigger);
+ }
+ }
+
+ /**
+ * Update the {@code result} camera metadata map with the new value for the
+ * {@code control.afState}.
+ *
+ * <p>AF callbacks are processed in the background, and each call to {@link #mapResultTriggers}
+ * will have the latest AF state as reflected by the camera1 callbacks.</p>
+ *
+ * @param result a non-{@code null} result
+ */
+ public void mapResultTriggers(CameraMetadataNative result) {
+ checkNotNull(result, "result must not be null");
+
+ int newAfState;
+ synchronized (mLock) {
+ newAfState = mAfState;
+ }
+
+ if (VERBOSE && newAfState != mAfStatePrevious) {
+ Log.v(TAG, String.format("mapResultTriggers - afState changed from %s to %s",
+ afStateToString(mAfStatePrevious), afStateToString(newAfState)));
+ }
+
+ result.set(CaptureResult.CONTROL_AF_STATE, newAfState);
+
+ mAfStatePrevious = newAfState;
+ }
+
+ private static String afStateToString(int afState) {
+ switch (afState) {
+ case CONTROL_AF_STATE_ACTIVE_SCAN:
+ return "ACTIVE_SCAN";
+ case CONTROL_AF_STATE_FOCUSED_LOCKED:
+ return "FOCUSED_LOCKED";
+ case CONTROL_AF_STATE_INACTIVE:
+ return "INACTIVE";
+ case CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+ return "NOT_FOCUSED_LOCKED";
+ case CONTROL_AF_STATE_PASSIVE_FOCUSED:
+ return "PASSIVE_FOCUSED";
+ case CONTROL_AF_STATE_PASSIVE_SCAN:
+ return "PASSIVE_SCAN";
+ case CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
+ return "PASSIVE_UNFOCUSED";
+ default :
+ return "UNKNOWN(" + afState + ")";
+ }
+ }
+}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 157c159..633bada 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -48,6 +48,7 @@
* Provide legacy-specific implementations of camera2 metadata for legacy devices, such as the
* camera characteristics.
*/
+@SuppressWarnings("deprecation")
public class LegacyMetadataMapper {
private static final String TAG = "LegacyMetadataMapper";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -87,8 +88,8 @@
*/
static final boolean LIE_ABOUT_AE_STATE = false;
static final boolean LIE_ABOUT_AE_MAX_REGIONS = false;
- static final boolean LIE_ABOUT_AF = true;
- static final boolean LIE_ABOUT_AF_MAX_REGIONS = true;
+ static final boolean LIE_ABOUT_AF = false;
+ static final boolean LIE_ABOUT_AF_MAX_REGIONS = false;
static final boolean LIE_ABOUT_AWB_STATE = false;
static final boolean LIE_ABOUT_AWB = true;
@@ -162,6 +163,10 @@
*/
mapControlAe(m, p);
/*
+ * control.af*
+ */
+ mapControlAf(m, p);
+ /*
* control.awb*
*/
mapControlAwb(m, p);
@@ -379,6 +384,54 @@
}
}
+
+ @SuppressWarnings({"unchecked"})
+ private static void mapControlAf(CameraMetadataNative m, Camera.Parameters p) {
+ /*
+ * control.afAvailableModes
+ */
+ {
+ List<String> focusModes = p.getSupportedFocusModes();
+
+ String[] focusModeStrings = new String[] {
+ Camera.Parameters.FOCUS_MODE_AUTO,
+ Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
+ Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
+ Camera.Parameters.FOCUS_MODE_EDOF,
+ Camera.Parameters.FOCUS_MODE_INFINITY,
+ Camera.Parameters.FOCUS_MODE_MACRO,
+ Camera.Parameters.FOCUS_MODE_FIXED,
+ };
+
+ int[] focusModeInts = new int[] {
+ CONTROL_AF_MODE_AUTO,
+ CONTROL_AF_MODE_CONTINUOUS_PICTURE,
+ CONTROL_AF_MODE_CONTINUOUS_VIDEO,
+ CONTROL_AF_MODE_EDOF,
+ CONTROL_AF_MODE_OFF,
+ CONTROL_AF_MODE_MACRO,
+ CONTROL_AF_MODE_OFF
+ };
+
+ List<Integer> afAvail = ArrayUtils.convertStringListToIntList(
+ focusModes, focusModeStrings, focusModeInts);
+
+ // No AF modes supported? That's unpossible!
+ if (afAvail == null || afAvail.size() == 0) {
+ Log.w(TAG, "No AF modes supported (HAL bug); defaulting to AF_MODE_OFF only");
+ afAvail = new ArrayList<Integer>(/*capacity*/1);
+ afAvail.add(CONTROL_AF_MODE_OFF);
+ }
+
+ m.set(CONTROL_AF_AVAILABLE_MODES, ArrayUtils.toIntArray(afAvail));
+
+ if (VERBOSE) {
+ Log.v(TAG, "mapControlAf - control.afAvailableModes set to " +
+ ListUtils.listToString(afAvail));
+ }
+ }
+ }
+
private static void mapControlAwb(CameraMetadataNative m, Camera.Parameters p) {
if (!LIE_ABOUT_AWB) {
throw new AssertionError("Not implemented yet");
@@ -548,7 +601,6 @@
CaptureResult.JPEG_QUALITY ,
CaptureResult.JPEG_THUMBNAIL_QUALITY ,
CaptureResult.LENS_FOCAL_LENGTH ,
- CaptureResult.REQUEST_FRAME_COUNT ,
CaptureResult.REQUEST_PIPELINE_DEPTH ,
CaptureResult.SCALER_CROP_REGION ,
CaptureResult.SENSOR_TIMESTAMP ,
@@ -794,4 +846,55 @@
return tags;
}
+
+ /**
+ * Convert the requested AF mode into its equivalent supported parameter.
+ *
+ * @param mode {@code CONTROL_AF_MODE}
+ * @param supportedFocusModes list of camera1's supported focus modes
+ * @return the stringified af mode, or {@code null} if its not supported
+ */
+ static String convertAfModeToLegacy(int mode, List<String> supportedFocusModes) {
+ if (supportedFocusModes == null || supportedFocusModes.isEmpty()) {
+ Log.w(TAG, "No focus modes supported; API1 bug");
+ return null;
+ }
+
+ String param = null;
+ switch (mode) {
+ case CONTROL_AF_MODE_AUTO:
+ param = Parameters.FOCUS_MODE_AUTO;
+ break;
+ case CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+ param = Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
+ break;
+ case CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+ param = Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
+ break;
+ case CONTROL_AF_MODE_EDOF:
+ param = Parameters.FOCUS_MODE_EDOF;
+ break;
+ case CONTROL_AF_MODE_MACRO:
+ param = Parameters.FOCUS_MODE_MACRO;
+ break;
+ case CONTROL_AF_MODE_OFF:
+ if (supportedFocusModes.contains(Parameters.FOCUS_MODE_FIXED)) {
+ param = Parameters.FOCUS_MODE_FIXED;
+ } else {
+ param = Parameters.FOCUS_MODE_INFINITY;
+ }
+ }
+
+ if (!supportedFocusModes.contains(param)) {
+ // Weed out bad user input by setting to the first arbitrary focus mode
+ String defaultMode = supportedFocusModes.get(0);
+ Log.w(TAG,
+ String.format(
+ "convertAfModeToLegacy - ignoring unsupported mode %d, " +
+ "defaulting to %s", mode, defaultMode));
+ param = defaultMode;
+ }
+
+ return param;
+ }
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
index 4a9afa6..fbfc39f 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
@@ -23,6 +23,7 @@
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.utils.ListUtils;
+import android.hardware.camera2.utils.ParamsUtils;
import android.util.Log;
import android.util.Range;
import android.util.Size;
@@ -38,6 +39,7 @@
/**
* Provide legacy-specific implementations of camera2 CaptureRequest for legacy devices.
*/
+@SuppressWarnings("deprecation")
public class LegacyRequestMapper {
private static final String TAG = "LegacyRequestMapper";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -158,7 +160,7 @@
{
Range<Integer> compensationRange =
characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
- int compensation = getOrDefault(request,
+ int compensation = ParamsUtils.getOrDefault(request,
CONTROL_AE_EXPOSURE_COMPENSATION,
/*defaultValue*/0);
@@ -192,6 +194,23 @@
// control.aeMode, flash.mode
mapAeAndFlashMode(request, /*out*/params);
+ // control.afMode
+ {
+ int afMode = ParamsUtils.getOrDefault(request, CONTROL_AF_MODE,
+ /*defaultValue*/CONTROL_AF_MODE_OFF);
+ String focusMode = LegacyMetadataMapper.convertAfModeToLegacy(afMode,
+ params.getSupportedFocusModes());
+
+ if (focusMode != null) {
+ params.setFocusMode(focusMode);
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "convertRequestToMetadata - control.afMode "
+ + afMode + " mapped to " + focusMode);
+ }
+ }
+
// control.awbLock
{
Boolean awbLock = getIfSupported(request, CONTROL_AWB_LOCK, /*defaultValue*/false,
@@ -204,6 +223,21 @@
// TODO: Don't add control.awbLock to availableRequestKeys if it's not supported
}
+
+ // lens.focusDistance
+ {
+ boolean infinityFocusSupported =
+ ListUtils.listContains(params.getSupportedFocusModes(),
+ Parameters.FOCUS_MODE_INFINITY);
+ Float focusDistance = getIfSupported(request, LENS_FOCUS_DISTANCE,
+ /*defaultValue*/0f, infinityFocusSupported, /*allowedValue*/0f);
+
+ if (focusDistance == null || focusDistance != 0f) {
+ Log.w(TAG,
+ "convertRequestToMetadata - Ignoring android.lens.focusDistance "
+ + infinityFocusSupported + ", only 0.0f is supported");
+ }
+ }
}
private static List<Camera.Area> convertMeteringRegionsToLegacy(
@@ -253,8 +287,8 @@
}
private static void mapAeAndFlashMode(CaptureRequest r, /*out*/Parameters p) {
- int flashMode = getOrDefault(r, FLASH_MODE, FLASH_MODE_OFF);
- int aeMode = getOrDefault(r, CONTROL_AE_MODE, CONTROL_AE_MODE_ON);
+ int flashMode = ParamsUtils.getOrDefault(r, FLASH_MODE, FLASH_MODE_OFF);
+ int aeMode = ParamsUtils.getOrDefault(r, CONTROL_AE_MODE, CONTROL_AE_MODE_ON);
List<String> supportedFlashModes = p.getSupportedFlashModes();
@@ -355,20 +389,6 @@
return legacyFps;
}
- /** Return the value set by the key, or the {@code defaultValue} if no value was set. */
- private static <T> T getOrDefault(CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue) {
- checkNotNull(r, "r must not be null");
- checkNotNull(key, "key must not be null");
- checkNotNull(defaultValue, "defaultValue must not be null");
-
- T value = r.get(key);
- if (value == null) {
- return defaultValue;
- } else {
- return value;
- }
- }
-
/**
* Return {@code null} if the value is not supported, otherwise return the retrieved key's
* value from the request (or the default value if it wasn't set).
@@ -382,7 +402,7 @@
private static <T> T getIfSupported(
CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue, boolean isSupported,
T allowedValue) {
- T val = getOrDefault(r, key, defaultValue);
+ T val = ParamsUtils.getOrDefault(r, key, defaultValue);
if (!isSupported) {
if (!Objects.equals(val, allowedValue)) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index 88f95e1..07852b9 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -34,11 +34,13 @@
import java.util.ArrayList;
import java.util.List;
+import static com.android.internal.util.Preconditions.*;
import static android.hardware.camera2.CaptureResult.*;
/**
* Provide legacy-specific implementations of camera2 CaptureResult for legacy devices.
*/
+@SuppressWarnings("deprecation")
public class LegacyResultMapper {
private static final String TAG = "LegacyResultMapper";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -60,17 +62,43 @@
*/
public CameraMetadataNative cachedConvertResultMetadata(
LegacyRequest legacyRequest, long timestamp) {
- if (mCachedRequest != null && legacyRequest.parameters.same(mCachedRequest.parameters)) {
- CameraMetadataNative newResult = new CameraMetadataNative(mCachedResult);
+ CameraMetadataNative result;
+ boolean cached;
- // sensor.timestamp
- newResult.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
- return newResult;
+ /*
+ * Attempt to look up the result from the cache if the parameters haven't changed
+ */
+ if (mCachedRequest != null && legacyRequest.parameters.same(mCachedRequest.parameters)) {
+ result = new CameraMetadataNative(mCachedResult);
+ cached = true;
+ } else {
+ result = convertResultMetadata(legacyRequest, timestamp);
+ cached = false;
+
+ // Always cache a *copy* of the metadata result,
+ // since api2's client side takes ownership of it after it receives a result
+ mCachedRequest = legacyRequest;
+ mCachedResult = new CameraMetadataNative(result);
}
- mCachedRequest = legacyRequest;
- mCachedResult = convertResultMetadata(mCachedRequest, timestamp);
- return mCachedResult;
+ /*
+ * Unconditionally set fields that change in every single frame
+ */
+ {
+ // sensor.timestamp
+ result.set(SENSOR_TIMESTAMP, timestamp);
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "cachedConvertResultMetadata - cached? " + cached +
+ " timestamp = " + timestamp);
+
+ Log.v(TAG, "----- beginning of result dump ------");
+ result.dumpToLog();
+ Log.v(TAG, "----- end of result dump ------");
+ }
+
+ return result;
}
/**
@@ -81,7 +109,7 @@
*
* @return a {@link CameraMetadataNative} object containing result metadata.
*/
- public static CameraMetadataNative convertResultMetadata(LegacyRequest legacyRequest,
+ private static CameraMetadataNative convertResultMetadata(LegacyRequest legacyRequest,
long timestamp) {
CameraCharacteristics characteristics = legacyRequest.characteristics;
CaptureRequest request = legacyRequest.captureRequest;
@@ -98,17 +126,15 @@
/*
* control
*/
- // control.afState
- if (LegacyMetadataMapper.LIE_ABOUT_AF) {
- // TODO: Implement autofocus state machine
- result.set(CaptureResult.CONTROL_AF_MODE, request.get(CaptureRequest.CONTROL_AF_MODE));
- }
/*
* control.ae*
*/
mapAe(result, characteristics, request, activeArraySize, zoomData, /*out*/params);
+ // control.afMode
+ result.set(CaptureResult.CONTROL_AF_MODE, convertLegacyAfMode(params.getFocusMode()));
+
// control.awbLock
result.set(CaptureResult.CONTROL_AWB_LOCK, params.getAutoWhiteBalanceLock());
@@ -137,10 +163,24 @@
/*
* lens
*/
+ // lens.focusDistance
+ {
+ if (Parameters.FOCUS_MODE_INFINITY.equals(params.getFocusMode())) {
+ result.set(CaptureResult.LENS_FOCUS_DISTANCE, 0.0f);
+ }
+ }
+
// lens.focalLength
result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
/*
+ * request
+ */
+ // request.pipelineDepth
+ result.set(REQUEST_PIPELINE_DEPTH,
+ characteristics.get(CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH));
+
+ /*
* scaler
*/
mapScaler(result, zoomData, /*out*/params);
@@ -148,8 +188,6 @@
/*
* sensor
*/
- // sensor.timestamp
- result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
// TODO: Remaining result metadata tags conversions.
return result;
@@ -301,6 +339,33 @@
m.set(CONTROL_AE_MODE, aeMode);
}
+ private static int convertLegacyAfMode(String mode) {
+ if (mode == null) {
+ Log.w(TAG, "convertLegacyAfMode - no AF mode, default to OFF");
+ return CONTROL_AF_MODE_OFF;
+ }
+
+ switch (mode) {
+ case Parameters.FOCUS_MODE_AUTO:
+ return CONTROL_AF_MODE_AUTO;
+ case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
+ return CONTROL_AF_MODE_CONTINUOUS_PICTURE;
+ case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
+ return CONTROL_AF_MODE_CONTINUOUS_VIDEO;
+ case Parameters.FOCUS_MODE_EDOF:
+ return CONTROL_AF_MODE_EDOF;
+ case Parameters.FOCUS_MODE_MACRO:
+ return CONTROL_AF_MODE_MACRO;
+ case Parameters.FOCUS_MODE_FIXED:
+ return CONTROL_AF_MODE_OFF;
+ case Parameters.FOCUS_MODE_INFINITY:
+ return CONTROL_AF_MODE_OFF;
+ default:
+ Log.w(TAG, "convertLegacyAfMode - unknown mode " + mode + " , ignoring");
+ return CONTROL_AF_MODE_OFF;
+ }
+ }
+
/** Map results for scaler.* */
private static void mapScaler(CameraMetadataNative m,
ZoomData zoomData,
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 066b416..c556c32 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -28,6 +28,7 @@
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
+import android.util.MutableLong;
import android.util.Pair;
import android.util.Size;
import android.view.Surface;
@@ -65,6 +66,7 @@
private final CameraDeviceState mDeviceState;
private final CaptureCollector mCaptureCollector;
+ private final LegacyFocusStateMapper mFocusStateMapper;
private static final int MSG_CONFIGURE_OUTPUTS = 1;
private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2;
@@ -74,6 +76,7 @@
private static final int PREVIEW_FRAME_TIMEOUT = 300; // ms
private static final int JPEG_FRAME_TIMEOUT = 3000; // ms (same as CTS for API2)
+ private static final int REQUEST_COMPLETE_TIMEOUT = 3000; // ms (same as JPEG timeout)
private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
private boolean mPreviewRunning = false;
@@ -510,7 +513,7 @@
private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
private boolean mCleanup = false;
- private LegacyResultMapper mMapper = new LegacyResultMapper();
+ private final LegacyResultMapper mMapper = new LegacyResultMapper();
@Override
public boolean handleMessage(Message msg) {
@@ -586,6 +589,8 @@
CaptureRequest request = holder.getRequest();
boolean paramsChanged = false;
+
+ // Lazily process the rest of the request
if (mLastRequest == null || mLastRequest.captureRequest != request) {
// The intermediate buffer is sometimes null, but we always need
@@ -608,6 +613,10 @@
}
}
+ // Unconditionally process AF triggers, since they're non-idempotent
+ // - must be done after setting the most-up-to-date AF mode
+ mFocusStateMapper.processRequestTriggers(request, mParams);
+
try {
boolean success = mCaptureCollector.queueRequest(holder,
mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
@@ -649,6 +658,27 @@
// Update parameters to the latest that we think the camera is using
mLastRequest.setParameters(mParams);
}
+
+ MutableLong timestampMutable = new MutableLong(/*value*/0L);
+ try {
+ boolean success = mCaptureCollector.waitForRequestCompleted(holder,
+ REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
+ /*out*/timestampMutable);
+
+ if (!success) {
+ Log.e(TAG, "Timed out while waiting for request to complete.");
+ }
+ } catch (InterruptedException e) {
+ // TODO: report error to CameraDevice
+ Log.e(TAG, "Interrupted during request completition.", e);
+ }
+
+ CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
+ mLastRequest, timestampMutable.value);
+ // Update AF state
+ mFocusStateMapper.mapResultTriggers(result);
+
+ mDeviceState.setCaptureResult(holder, result);
}
if (DEBUG) {
long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
@@ -700,6 +730,7 @@
String name = String.format("RequestThread-%d", cameraId);
TAG = name;
mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
+ mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index c31f59e..1efabb1 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -67,11 +67,6 @@
private static final String TAG = "StreamConfigurationMap";
/**
- * Indicates that a minimum frame duration is not available for a particular configuration.
- */
- public static final long NO_MIN_FRAME_DURATION = 0;
-
- /**
* Create a new {@link StreamConfigurationMap}.
*
* <p>The array parameters ownership is passed to this object after creation; do not
@@ -536,7 +531,7 @@
*
* <p>For devices that do not support manual sensor control
* ({@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR}),
- * this function may return {@link #NO_MIN_FRAME_DURATION}.</p>
+ * this function may return 0.</p>
*
* <!--
* TODO: uncomment after adding input stream support
@@ -547,7 +542,7 @@
* @param format an image format from {@link ImageFormat} or {@link PixelFormat}
* @param size an output-compatible size
* @return a minimum frame duration {@code >} 0 in nanoseconds, or
- * {@link #NO_MIN_FRAME_DURATION} if the minimum frame duration is not available.
+ * 0 if the minimum frame duration is not available.
*
* @throws IllegalArgumentException if {@code format} or {@code size} was not supported
* @throws NullPointerException if {@code size} was {@code null}
@@ -586,7 +581,7 @@
*
* <p>For devices that do not support manual sensor control
* ({@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR}),
- * this function may return {@link #NO_MIN_FRAME_DURATION}.</p>
+ * this function may return 0.</p>
*
* <!--
* TODO: uncomment after adding input stream support
@@ -599,7 +594,7 @@
* non-empty array returned by {@link #getOutputSizes(Class)}
* @param size an output-compatible size
* @return a minimum frame duration {@code >} 0 in nanoseconds, or
- * {@link #NO_MIN_FRAME_DURATION} if the minimum frame duration is not available.
+ * 0 if the minimum frame duration is not available.
*
* @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
* @throws NullPointerException if {@code size} or {@code klass} was {@code null}
@@ -1065,8 +1060,8 @@
return configurationDuration.getDuration();
}
}
-
- return getDurationDefault(duration);
+ // Default duration is '0' (unsupported/no extra stall)
+ return 0;
}
/**
@@ -1086,17 +1081,6 @@
}
}
- private long getDurationDefault(int duration) {
- switch (duration) {
- case DURATION_MIN_FRAME:
- return NO_MIN_FRAME_DURATION;
- case DURATION_STALL:
- return 0L; // OK. A lack of a stall duration implies a 0 stall duration
- default:
- throw new IllegalArgumentException("duration was invalid");
- }
- }
-
/** Count the number of publicly-visible output formats */
private int getPublicFormatCount(boolean output) {
HashMap<Integer, Integer> formatsMap = getFormatsMap(output);
diff --git a/core/java/android/hardware/camera2/utils/ArrayUtils.java b/core/java/android/hardware/camera2/utils/ArrayUtils.java
index c5a56cd..24c85d0 100644
--- a/core/java/android/hardware/camera2/utils/ArrayUtils.java
+++ b/core/java/android/hardware/camera2/utils/ArrayUtils.java
@@ -67,6 +67,36 @@
return null;
}
+ List<Integer> convertedList = convertStringListToIntList(list, convertFrom, convertTo);
+
+ int[] returnArray = new int[convertedList.size()];
+ for (int i = 0; i < returnArray.length; ++i) {
+ returnArray[i] = convertedList.get(i);
+ }
+
+ return returnArray;
+ }
+
+ /**
+ * Create an {@code List<Integer>} from the {@code List<>} by using {@code convertFrom} and
+ * {@code convertTo} as a one-to-one map (via the index).
+ *
+ * <p>Strings not appearing in {@code convertFrom} are ignored (with a logged warning);
+ * strings appearing in {@code convertFrom} but not {@code convertTo} are silently
+ * dropped.</p>
+ *
+ * @param list Source list of strings
+ * @param convertFrom Conversion list of strings
+ * @param convertTo Conversion list of ints
+ * @return A list of ints where the values correspond to the ones in {@code convertTo}
+ * or {@code null} if {@code list} was {@code null}
+ */
+ public static List<Integer> convertStringListToIntList(
+ List<String> list, String[] convertFrom, int[] convertTo) {
+ if (list == null) {
+ return null;
+ }
+
List<Integer> convertedList = new ArrayList<>(list.size());
for (String str : list) {
@@ -84,12 +114,33 @@
}
}
- int[] returnArray = new int[convertedList.size()];
- for (int i = 0; i < returnArray.length; ++i) {
- returnArray[i] = convertedList.get(i);
+ return convertedList;
+ }
+
+ /**
+ * Convert the list of integers in {@code list} to an {@code int} array.
+ *
+ * <p>Every element in {@code list} must be non-{@code null}.</p>
+ *
+ * @param list a list of non-{@code null} integers
+ *
+ * @return a new int array containing all the elements from {@code list}
+ *
+ * @throws NullPointerException if any of the elements in {@code list} were {@code null}
+ */
+ public static int[] toIntArray(List<Integer> list) {
+ if (list == null) {
+ return null;
}
- return returnArray;
+ int[] arr = new int[list.size()];
+ int i = 0;
+ for (int elem : list) {
+ arr[i] = elem;
+ i++;
+ }
+
+ return arr;
}
private ArrayUtils() {
diff --git a/core/java/android/hardware/camera2/utils/ParamsUtils.java b/core/java/android/hardware/camera2/utils/ParamsUtils.java
index 232a4f6..976fa2e 100644
--- a/core/java/android/hardware/camera2/utils/ParamsUtils.java
+++ b/core/java/android/hardware/camera2/utils/ParamsUtils.java
@@ -19,6 +19,7 @@
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.hardware.camera2.CaptureRequest;
import android.util.Rational;
import android.util.Size;
@@ -175,6 +176,24 @@
destination.top = source.top;
}
+ /**
+ * Return the value set by the key, or the {@code defaultValue} if no value was set.
+ *
+ * @throws NullPointerException if any of the args were {@code null}
+ */
+ public static <T> T getOrDefault(CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue) {
+ checkNotNull(r, "r must not be null");
+ checkNotNull(key, "key must not be null");
+ checkNotNull(defaultValue, "defaultValue must not be null");
+
+ T value = r.get(key);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ return value;
+ }
+ }
+
private ParamsUtils() {
throw new AssertionError();
}
diff --git a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
index d663714..27829a7 100644
--- a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
@@ -24,6 +24,8 @@
* A class to encapsulate device information for HDMI-CEC. This container
* include basic information such as logical address, physical address and
* device type, and additional information like vendor id and osd name.
+ * Also used to keep the information of non-CEC devices for which only
+ * port ID, physical address are meaningful.
*
* @hide
*/
@@ -71,6 +73,7 @@
private final int mDeviceType;
private final int mVendorId;
private final String mDisplayName;
+ private final boolean mIsCecDevice;
/**
* A helper class to deserialize {@link HdmiCecDeviceInfo} for a parcel.
@@ -96,7 +99,7 @@
};
/**
- * Constructor.
+ * Constructor. Used to initialize the instance for CEC device.
*
* @param logicalAddress logical address of HDMI-CEC device
* @param physicalAddress physical address of HDMI-CEC device
@@ -114,6 +117,24 @@
mDeviceType = deviceType;
mDisplayName = displayName;
mVendorId = vendorId;
+ mIsCecDevice = true;
+ }
+
+ /**
+ * Constructor. Used to initialize the instance for non-CEC device.
+ *
+ * @param physicalAddress physical address of HDMI device
+ * @param portId HDMI port ID (1 for HDMI1)
+ * @hide
+ */
+ public HdmiCecDeviceInfo(int physicalAddress, int portId) {
+ mLogicalAddress = -1;
+ mPhysicalAddress = physicalAddress;
+ mPortId = portId;
+ mDeviceType = DEVICE_RESERVED;
+ mDisplayName = null;
+ mVendorId = 0;
+ mIsCecDevice = false;
}
/**
@@ -155,6 +176,14 @@
}
/**
+ * Return {@code true} if the device represents an HDMI-CEC device. {@code false}
+ * if the device is either MHL or non-CEC device.
+ */
+ public boolean isCecDevice() {
+ return mIsCecDevice;
+ }
+
+ /**
* Return display (OSD) name of the device.
*/
public String getDisplayName() {
@@ -199,12 +228,19 @@
@Override
public String toString() {
StringBuffer s = new StringBuffer();
- s.append("logical_address: ").append(mLogicalAddress).append(", ");
- s.append("physical_address: ").append(mPhysicalAddress).append(", ");
- s.append("port_id: ").append(mPortId).append(", ");
- s.append("device_type: ").append(mDeviceType).append(", ");
- s.append("vendor_id: ").append(mVendorId).append(", ");
- s.append("display_name: ").append(mDisplayName);
+ if (isCecDevice()) {
+ s.append("CEC: ");
+ s.append("logical_address: ").append(mLogicalAddress).append(", ");
+ s.append("physical_address: ").append(mPhysicalAddress).append(", ");
+ s.append("port_id: ").append(mPortId).append(", ");
+ s.append("device_type: ").append(mDeviceType).append(", ");
+ s.append("vendor_id: ").append(mVendorId).append(", ");
+ s.append("display_name: ").append(mDisplayName);
+ } else {
+ s.append("Non-CEC: ");
+ s.append("physical_address: ").append(mPhysicalAddress).append(", ");
+ s.append("port_id: ").append(mPortId).append(", ");
+ }
return s.toString();
}
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
index 626bd2a..43869264 100644
--- a/core/java/android/net/INetworkScoreService.aidl
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -48,6 +48,12 @@
boolean setActiveScorer(in String packageName);
/**
+ * Disable the current active scorer and clear existing scores.
+ * @throws SecurityException if the caller is not the current scorer or the system.
+ */
+ void disableScoring();
+
+ /**
* Register a network subsystem for scoring.
*
* @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index b497c6e..9215853 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -205,6 +205,20 @@
}
/**
+ * Turn off network scoring.
+ *
+ * <p>May only be called by the current scorer app, or the system.
+ *
+ * @throws SecurityException if the caller is neither the active scorer nor the system.
+ */
+ public void disableScoring() throws SecurityException {
+ try {
+ mService.disableScoring();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Request scoring for networks.
*
* <p>Note that this is just a helper method to assemble the broadcast, and will run in the
@@ -222,6 +236,7 @@
}
Intent intent = new Intent(ACTION_SCORE_NETWORKS);
intent.setPackage(activeScorer);
+ intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(EXTRA_NETWORKS_TO_SCORE, networks);
mContext.sendBroadcast(intent);
return true;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index a506c42..9935317 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -615,6 +615,12 @@
if (guest != null) {
Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
+ try {
+ mService.setUserRestrictions(
+ mService.getDefaultGuestRestrictions(), guest.id);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not update guest restrictions");
+ }
}
return guest;
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 74bc186..c39ec97 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -19,7 +19,9 @@
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
+import android.content.Context;
import android.content.res.Configuration;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -204,6 +206,14 @@
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
+ } else {
+ // If there's no parent and we're running on L or above (or in the
+ // system context), assume we want hardware acceleration.
+ final Context context = view.getContext();
+ if (context != null
+ && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
+ wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ }
}
ViewRootImpl root;
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
index 81f4baf..35da4bc 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
@@ -96,38 +96,56 @@
@SmallTest
public void testsetServiceDataFilter() {
byte[] setServiceData = new byte[] {
- 0x0b, 0x11, 0x50, 0x64 };
+ 0x50, 0x64 };
ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
ScanFilter filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData).build();
assertTrue("service data filter fails", filter.matches(mScanResult));
- byte[] nonMatchData = new byte[] {
- 0x0b, 0x01, 0x50, 0x64 };
- filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData).build();
- assertFalse("service data filter fails", filter.matches(mScanResult));
+ byte[] emptyData = new byte[0];
+ filter = mFilterBuilder.setServiceData(serviceDataUuid, emptyData).build();
+ assertTrue("service data filter fails", filter.matches(mScanResult));
+ byte[] prefixData = new byte[] {
+ 0x50 };
+ filter = mFilterBuilder.setServiceData(serviceDataUuid, prefixData).build();
+ assertTrue("service data filter fails", filter.matches(mScanResult));
+
+ byte[] nonMatchData = new byte[] {
+ 0x51, 0x64 };
byte[] mask = new byte[] {
- (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF };
+ (byte) 0x00, (byte) 0xFF };
filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData, mask).build();
assertTrue("partial service data filter fails", filter.matches(mScanResult));
+
+ filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData).build();
+ assertFalse("service data filter fails", filter.matches(mScanResult));
}
@SmallTest
public void testManufacturerSpecificData() {
byte[] setManufacturerData = new byte[] {
- (byte) 0xE0, 0x00, 0x02, 0x15 };
- int manufacturerId = 224;
+ 0x02, 0x15 };
+ int manufacturerId = 0xE0;
ScanFilter filter =
mFilterBuilder.setManufacturerData(manufacturerId, setManufacturerData).build();
- assertTrue("setManufacturerData filter fails", filter.matches(mScanResult));
+ assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
+ byte[] emptyData = new byte[0];
+ filter = mFilterBuilder.setManufacturerData(manufacturerId, emptyData).build();
+ assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
+
+ byte[] prefixData = new byte[] {
+ 0x02 };
+ filter = mFilterBuilder.setManufacturerData(manufacturerId, prefixData).build();
+ assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
+
+ // Test data mask
byte[] nonMatchData = new byte[] {
- (byte) 0xF0, 0x00, 0x02, 0x15 };
+ 0x02, 0x14 };
filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData).build();
- assertFalse("setManufacturerData filter fails", filter.matches(mScanResult));
-
+ assertFalse("manufacturer data filter fails", filter.matches(mScanResult));
byte[] mask = new byte[] {
- (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
+ (byte) 0xFF, (byte) 0x00
};
filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData, mask).build();
assertTrue("partial setManufacturerData filter fails", filter.matches(mScanResult));
@@ -153,27 +171,33 @@
ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build();
testReadWriteParcelForFilter(filter);
- byte[] setServiceData = new byte[] {
- 0x0b, 0x11, 0x50, 0x64 };
+ byte[] serviceData = new byte[] {
+ 0x50, 0x64 };
ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
- filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData).build();
+ filter = mFilterBuilder.setServiceData(serviceDataUuid, serviceData).build();
+ testReadWriteParcelForFilter(filter);
+
+ filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build();
testReadWriteParcelForFilter(filter);
byte[] serviceDataMask = new byte[] {
- (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF };
- filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData, serviceDataMask)
+ (byte) 0xFF, (byte) 0xFF };
+ filter = mFilterBuilder.setServiceData(serviceDataUuid, serviceData, serviceDataMask)
.build();
testReadWriteParcelForFilter(filter);
byte[] manufacturerData = new byte[] {
- (byte) 0xE0, 0x00, 0x02, 0x15 };
- int manufacturerId = 224;
+ 0x02, 0x15 };
+ int manufacturerId = 0xE0;
filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData).build();
testReadWriteParcelForFilter(filter);
+ filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build();
+ testReadWriteParcelForFilter(filter);
+
byte[] manufacturerDataMask = new byte[] {
- (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
+ (byte) 0xFF, (byte) 0xFF
};
filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData,
manufacturerDataMask).build();
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 2962460..2fa0c93 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2161,7 +2161,7 @@
* @param fd the FileDescriptor for the file you want to play
* @param offset the offset into the file where the data to be played starts, in bytes
* @param length the length in bytes of the data to be played
- * @param mimeType The mime type of the file. Must be one of the mime types listed above.
+ * @param mime The mime type of the file. Must be one of the mime types listed above.
* @throws IllegalArgumentException if the mimeType is not supported.
* @throws IllegalStateException if called in an invalid state.
*/
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 3861cc1..915e2f9 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -11,7 +11,7 @@
<activity
android:name=".DocumentsActivity"
- android:theme="@style/Theme"
+ android:theme="@style/DocumentsTheme"
android:icon="@drawable/ic_doc_text">
<intent-filter>
<action android:name="android.intent.action.OPEN_DOCUMENT" />
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow_am.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow_am.9.png
deleted file mode 100644
index 904d525..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album.png
index e462727..bf0ac6c 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk.png
index 8ac89b6..d4de3bd3 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_dark_am.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_dark_am.png
index 3cbff21..1e3a623 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_dark_am.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_dark_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_light_am.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_light_am.png
index 7c8b540..7d320fc 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_light_am.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_light_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_certificate.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_certificate.png
index 8644171..250208d 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_certificate.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_certificate.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_codes.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_codes.png
index 0a3a9a6..1bbdb21 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_codes.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_codes.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_compressed.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_compressed.png
index d37d9bf..124486d 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_compressed.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_compressed.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_contact.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_contact.png
new file mode 100644
index 0000000..f1af4bd
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_contact.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_event.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_event.png
new file mode 100644
index 0000000..9639efc
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_event.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_excel.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_excel.png
index 0f5159b..c744499 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_excel.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_excel.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_font.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_font.png
index b3e7192..206d95f 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_font.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_font.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_generic.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_generic.png
new file mode 100644
index 0000000..484288a
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_generic.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_image_dark.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_image_dark.png
index 10c0b99..d2e5e6a 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_image_dark.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_image_dark.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_image_light.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_image_light.png
index c857519..a1badcf 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_image_light.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_image_light.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_pdf.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_pdf.png
index b168481..1e9ad7b 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_pdf.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_pdf.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_powerpoint.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_powerpoint.png
index 4af36b7..fa425d3 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_powerpoint.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_powerpoint.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_presentation.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_presentation.png
index 9167c7d..5274bb3 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_presentation.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_presentation.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_spreadsheet.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_spreadsheet.png
new file mode 100644
index 0000000..82ffc32
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_spreadsheet.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_text.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_text.png
new file mode 100644
index 0000000..a741027
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_text.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_video_dark_am.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_video_dark_am.png
index ff8d3aa..f2cad34 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_video_dark_am.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_video_dark_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_video_light_am.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_video_light_am.png
index d422eb7..75c1f0d 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_video_light_am.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_video_light_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_word.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_word.png
index 3860241..9d5b864 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_word.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_word.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_glyph.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_glyph.png
deleted file mode 100644
index 251ecfb..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_glyph.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png
deleted file mode 100644
index 0d75172..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_shadow_am.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_shadow_am.9.png
deleted file mode 100644
index 4a710ce..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_shadow_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_shadow_tablet_am.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_shadow_tablet_am.9.png
deleted file mode 100644
index a1bbc8b..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_shadow_tablet_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png
deleted file mode 100644
index 403eddb..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_grid_folder.png b/packages/DocumentsUI/res/drawable-hdpi/ic_grid_folder.png
index e9cfdb1..59bf931 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_grid_folder.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_grid_folder.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am.png
index ab4d176..503b982 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_rename_am.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_rename_am.png
index 4eeafad..ceeb8c6 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_rename_am.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_rename_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am.png
index 3563e97..0864867 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_undo_am.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_undo_am.png
index 2d61dc7..b66e252 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_undo_am.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_undo_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_dark.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_dark.png
new file mode 100644
index 0000000..92fa552
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_dark.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_light.png
similarity index 87%
rename from packages/DocumentsUI/res/drawable-hdpi/ic_root_usb.png
rename to packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_light.png
index 665ee0b..bf6e373 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_light.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_subdirectory_arrow_am.png b/packages/DocumentsUI/res/drawable-hdpi/ic_subdirectory_arrow_am.png
index 6be1f5b..61bb371 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_subdirectory_arrow_am.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_subdirectory_arrow_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow_am.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow_am.9.png
deleted file mode 100644
index 068619b..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album.png
index 0d3d5d7..d4f5814 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk.png
index f9597f0..37379f8 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_dark_am.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_dark_am.png
index deba408..994d04f 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_dark_am.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_dark_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_light_am.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_light_am.png
index 88f0127..00526e5 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_light_am.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_light_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_certificate.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_certificate.png
index b1323da..ca1e4ed 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_certificate.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_certificate.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_codes.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_codes.png
index ff99fd7..8ba2307 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_codes.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_codes.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_compressed.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_compressed.png
index 4af71f3..c0b9bce 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_compressed.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_compressed.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_contact.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_contact.png
new file mode 100644
index 0000000..da4d0ce
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_contact.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_event.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_event.png
new file mode 100644
index 0000000..ada1bfa
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_event.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_excel.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_excel.png
index 3adff71..822815d 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_excel.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_excel.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_font.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_font.png
index 0a4a841..97fe282 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_font.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_font.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_generic.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_generic.png
new file mode 100644
index 0000000..2a02045
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_generic.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_image_dark.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_image_dark.png
index f061b0c..58542b9 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_image_dark.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_image_dark.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_image_light.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_image_light.png
index fa3d2ea..dc94502 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_image_light.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_image_light.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_pdf.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_pdf.png
index b558d08..90d624d 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_pdf.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_pdf.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_powerpoint.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_powerpoint.png
index efc0b13..3b72269 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_powerpoint.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_powerpoint.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_presentation.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_presentation.png
index 655d866..72751e2 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_presentation.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_presentation.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_spreadsheet.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_spreadsheet.png
new file mode 100644
index 0000000..75de220
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_spreadsheet.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_text.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_text.png
new file mode 100644
index 0000000..dc4c7ac
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_text.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_video_dark_am.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_video_dark_am.png
index 1b6f00f..64f34c3 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_video_dark_am.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_video_dark_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_video_light_am.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_video_light_am.png
index 24dad11..955755e 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_video_light_am.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_video_light_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_word.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_word.png
index 7f26252..b56b2e9 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_word.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_word.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_glyph.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_glyph.png
deleted file mode 100644
index ae0da34..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_glyph.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png
deleted file mode 100644
index 0d75172..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_shadow_am.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_shadow_am.9.png
deleted file mode 100644
index 9343a39..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_shadow_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_shadow_tablet_am.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_shadow_tablet_am.9.png
deleted file mode 100644
index fabb56e..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_shadow_tablet_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png
deleted file mode 100644
index 9a9cf5e..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_grid_folder.png b/packages/DocumentsUI/res/drawable-mdpi/ic_grid_folder.png
index 5534c74e..9453739 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_grid_folder.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_grid_folder.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am.png
index 86b9256..fa934b3 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_rename_am.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_rename_am.png
index dc9bbc9..415134a 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_rename_am.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_rename_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am.png
index 86e1cf9..15fa247 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_undo_am.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_undo_am.png
index ed0ee5d..470dbf3 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_undo_am.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_undo_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_dark.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_dark.png
new file mode 100644
index 0000000..b11b60c
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_dark.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_light.png
similarity index 78%
rename from packages/DocumentsUI/res/drawable-mdpi/ic_root_usb.png
rename to packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_light.png
index a4f474b..5594a20 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_light.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_subdirectory_arrow_am.png b/packages/DocumentsUI/res/drawable-mdpi/ic_subdirectory_arrow_am.png
index c8c9f1a..43df5af 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_subdirectory_arrow_am.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_subdirectory_arrow_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow_am.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow_am.9.png
deleted file mode 100644
index e38a868..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album.png
index 8952222..b7aafac 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk.png
index bc833b4..01e761c 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_dark_am.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_dark_am.png
index c782d0f..5fb0de6 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_dark_am.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_dark_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_light_am.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_light_am.png
index fbc1e24..3600694 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_light_am.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_light_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_certificate.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_certificate.png
index de17d0b..0a9e49c 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_certificate.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_certificate.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_codes.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_codes.png
index d396745..b99e8cf 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_codes.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_codes.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_compressed.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_compressed.png
index 1627d8e..cf113e2 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_compressed.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_compressed.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_contact.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_contact.png
new file mode 100644
index 0000000..5c5d658
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_contact.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_event.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_event.png
new file mode 100644
index 0000000..15f864f
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_event.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_excel.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_excel.png
index 884dd58..f6b4862 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_excel.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_excel.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_font.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_font.png
index 9f70fb8..2419ef8 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_font.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_font.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_generic.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_generic.png
new file mode 100644
index 0000000..abcf342
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_generic.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_image_dark.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_image_dark.png
index 49e4d0a..da85005 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_image_dark.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_image_dark.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_image_light.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_image_light.png
index 09609e3..e3ed4ee 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_image_light.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_image_light.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_pdf.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_pdf.png
index cdb8471..7f713dc 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_pdf.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_pdf.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_powerpoint.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_powerpoint.png
index f319929..c69cd9a 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_powerpoint.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_powerpoint.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_presentation.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_presentation.png
index ee8f63c..c09c36d 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_presentation.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_presentation.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_spreadsheet.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_spreadsheet.png
new file mode 100644
index 0000000..f91abe85
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_spreadsheet.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_text.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_text.png
new file mode 100644
index 0000000..81d3267
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_text.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_video_dark_am.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_video_dark_am.png
index aaa951b..0d16830 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_video_dark_am.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_video_dark_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_video_light_am.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_video_light_am.png
index 1342087..b808ee1 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_video_light_am.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_video_light_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_word.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_word.png
index 8c883ad..a05532e 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_word.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_word.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_glyph.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_glyph.png
deleted file mode 100644
index 7402c6d..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_glyph.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png
deleted file mode 100644
index 0d75172..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_shadow_am.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_shadow_am.9.png
deleted file mode 100644
index 027c64a..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_shadow_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_shadow_tablet_am.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_shadow_tablet_am.9.png
deleted file mode 100644
index 2c39a67..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_shadow_tablet_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png
deleted file mode 100644
index 205c34b..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_folder.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_folder.png
index a1c9789..890fda9 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_folder.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_folder.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am.png
index bbdee7e..d2717ab 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_rename_am.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_rename_am.png
index 8ccca63..d87a0b1 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_rename_am.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_rename_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am.png
index 28b7d5c..902f6ab 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_undo_am.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_undo_am.png
index 1121c43..d4b0341 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_undo_am.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_undo_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_dark.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_dark.png
new file mode 100644
index 0000000..15014a5
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_dark.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_light.png
similarity index 86%
rename from packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb.png
rename to packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_light.png
index 6ccfb76..f16b81f 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_light.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_subdirectory_arrow_am.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_subdirectory_arrow_am.png
index 94a757a..ef81618 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_subdirectory_arrow_am.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_subdirectory_arrow_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow_am.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow_am.9.png
deleted file mode 100644
index 0b332e4..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album.png
index e7151cf..5487507 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk.png
index 5c10593..36ce5d4 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_dark_am.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_dark_am.png
index e96ed99..0e911f5 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_dark_am.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_dark_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_light_am.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_light_am.png
index cac2aaf..e96cec7 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_light_am.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_light_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_certificate.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_certificate.png
index 1ee0875..0bd6576 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_certificate.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_certificate.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_codes.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_codes.png
index 8d3dabf..27f551b 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_codes.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_codes.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_compressed.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_compressed.png
index c3e21ae..22ddd69 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_compressed.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_compressed.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_contact.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_contact.png
new file mode 100644
index 0000000..0decac87
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_contact.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_event.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_event.png
new file mode 100644
index 0000000..0d2a932
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_event.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_excel.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_excel.png
index 1a373f3..bbd6617 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_excel.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_excel.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_font.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_font.png
index b4308b4..817032b 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_font.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_font.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_generic.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_generic.png
new file mode 100644
index 0000000..ebb9cc2
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_generic.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_image_dark.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_image_dark.png
index 63e4255..6754891 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_image_dark.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_image_dark.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_image_light.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_image_light.png
index 6237ded..d802d7f 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_image_light.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_image_light.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_pdf.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_pdf.png
index 2c0a81e..3a7bb41 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_pdf.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_pdf.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_powerpoint.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_powerpoint.png
index 2fba5ed..2bbf482 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_powerpoint.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_powerpoint.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_presentation.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_presentation.png
index 3e391ce..7da0b8b 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_presentation.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_presentation.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_spreadsheet.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_spreadsheet.png
new file mode 100644
index 0000000..b1f2097
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_spreadsheet.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_text.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_text.png
new file mode 100644
index 0000000..210060e
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_text.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_video_dark_am.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_video_dark_am.png
index 48b4a72..2914c85 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_video_dark_am.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_video_dark_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_video_light_am.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_video_light_am.png
index 15d6c50..ab104b7 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_video_light_am.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_video_light_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_word.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_word.png
index 9ccf41e..6b58983 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_word.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_word.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_glyph.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_glyph.png
deleted file mode 100644
index 4160699..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_glyph.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png
deleted file mode 100644
index 32b5f98..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_shadow_am.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_shadow_am.9.png
deleted file mode 100644
index 1a59e1a8..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_shadow_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_shadow_tablet_am.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_shadow_tablet_am.9.png
deleted file mode 100644
index 3c95790..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_shadow_tablet_am.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png
deleted file mode 100644
index f47d50a..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_folder.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_folder.png
index 10cdd51..6215385 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_folder.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_folder.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am.png
index 4013b7c..c336147 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_rename_am.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_rename_am.png
index 0fef9d0..414e199 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_rename_am.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_rename_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am.png
index 89bf79f..473e2da 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_undo_am.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_undo_am.png
index 7acc684e..9c3cc28 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_undo_am.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_undo_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb.png
deleted file mode 100644
index c31b4dc..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_dark.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_dark.png
new file mode 100644
index 0000000..657a116
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_dark.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_light.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_light.png
new file mode 100644
index 0000000..75fb374
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_light.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_subdirectory_arrow_am.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_subdirectory_arrow_am.png
index d852b8ec..755a79c 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_subdirectory_arrow_am.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_subdirectory_arrow_am.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable/ic_dir_shadow.xml b/packages/DocumentsUI/res/drawable/ic_dir_shadow.xml
deleted file mode 100644
index 1153e69..0000000
--- a/packages/DocumentsUI/res/drawable/ic_dir_shadow.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2013, 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.
- */
--->
-
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_dir_shadow_am"
- android:autoMirrored="true">
-</nine-patch>
diff --git a/packages/DocumentsUI/res/drawable/ic_drawer_shadow.xml b/packages/DocumentsUI/res/drawable/ic_drawer_shadow.xml
deleted file mode 100644
index 8d457be..0000000
--- a/packages/DocumentsUI/res/drawable/ic_drawer_shadow.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2013, 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.
- */
--->
-
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_drawer_shadow_am"
- android:autoMirrored="true">
-</nine-patch>
diff --git a/packages/DocumentsUI/res/drawable/ic_drawer_shadow_tablet.xml b/packages/DocumentsUI/res/drawable/ic_drawer_shadow_tablet.xml
deleted file mode 100644
index 382ebff..0000000
--- a/packages/DocumentsUI/res/drawable/ic_drawer_shadow_tablet.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2013, 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.
- */
--->
-
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_drawer_shadow_tablet_am"
- android:autoMirrored="true">
-</nine-patch>
diff --git a/packages/DocumentsUI/res/drawable/item_activated.xml b/packages/DocumentsUI/res/drawable/item_activated.xml
index 6ffefdb..1b3f44a 100644
--- a/packages/DocumentsUI/res/drawable/item_activated.xml
+++ b/packages/DocumentsUI/res/drawable/item_activated.xml
@@ -15,7 +15,11 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true" android:state_activated="true" android:drawable="@color/accent_item_activated" />
- <item android:state_focused="false" android:state_activated="true" android:drawable="@color/accent_item_activated" />
+ <item android:state_focused="true" android:state_activated="true">
+ <color android:color="?android:attr/colorAccent" />
+ </item>
+ <item android:state_focused="false" android:state_activated="true">
+ <color android:color="?android:attr/colorAccent" />
+ </item>
<item android:drawable="@android:color/transparent" />
</selector>
diff --git a/packages/DocumentsUI/res/drawable/item_root.xml b/packages/DocumentsUI/res/drawable/item_activated_overlay.xml
similarity index 73%
rename from packages/DocumentsUI/res/drawable/item_root.xml
rename to packages/DocumentsUI/res/drawable/item_activated_overlay.xml
index 60d4ab0..83e4d7e 100644
--- a/packages/DocumentsUI/res/drawable/item_root.xml
+++ b/packages/DocumentsUI/res/drawable/item_activated_overlay.xml
@@ -15,8 +15,11 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true" android:drawable="@color/item_root_pressed" />
- <item android:state_activated="true" android:drawable="@color/item_root_focused" />
- <item android:state_focused="true" android:drawable="@color/item_root_focused" />
+ <item android:state_focused="true" android:state_activated="true">
+ <color android:color="@color/accent_color_overlay" />
+ </item>
+ <item android:state_focused="false" android:state_activated="true">
+ <color android:color="@color/accent_color_overlay" />
+ </item>
<item android:drawable="@android:color/transparent" />
</selector>
diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
index 5f1e432..95af7e9 100644
--- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
@@ -17,7 +17,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:foreground="@drawable/item_activated">
+ android:background="@drawable/item_activated">
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/res/layout-sw720dp/activity.xml b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
index 9286277..c33932d 100644
--- a/packages/DocumentsUI/res/layout-sw720dp/activity.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
@@ -17,44 +17,59 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="horizontal"
- android:baselineAligned="false">
+ android:orientation="vertical">
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:id="@+id/dialog_roots">
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:background="?android:attr/colorPrimary"
+ android:elevation="8dp"
+ android:theme="?android:attr/actionBarTheme">
+
+ <Spinner
+ android:id="@+id/stack"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"
+ android:overlapAnchor="true" />
+
+ </Toolbar>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:baselineAligned="false">
<FrameLayout
android:id="@+id/container_roots"
- android:layout_width="250dp"
+ android:layout_width="256dp"
android:layout_height="match_parent" />
- <ImageView
- android:layout_width="wrap_content"
+ <LinearLayout
+ android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_gravity="end"
- android:scaleType="fitXY"
- android:src="@drawable/ic_drawer_shadow_tablet" />
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:elevation="8dp"
+ android:background="@*android:color/material_grey_50">
- </FrameLayout>
+ <com.android.documentsui.DirectoryContainerView
+ android:id="@+id/container_directory"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="vertical">
+ <FrameLayout
+ android:id="@+id/container_save"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@*android:color/material_grey_50"
+ android:elevation="8dp" />
- <com.android.documentsui.DirectoryContainerView
- android:id="@+id/container_directory"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
-
- <FrameLayout
- android:id="@+id/container_save"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ </LinearLayout>
</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/activity.xml b/packages/DocumentsUI/res/layout/activity.xml
index 2ef7e9c..d580821 100644
--- a/packages/DocumentsUI/res/layout/activity.xml
+++ b/packages/DocumentsUI/res/layout/activity.xml
@@ -24,6 +24,23 @@
android:layout_height="match_parent"
android:orientation="vertical">
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:background="?android:attr/colorPrimary"
+ android:elevation="8dp"
+ android:theme="?android:attr/actionBarTheme">
+
+ <Spinner
+ android:id="@+id/stack"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"
+ android:overlapAnchor="true" />
+
+ </Toolbar>
+
<com.android.documentsui.DirectoryContainerView
android:id="@+id/container_directory"
android:layout_width="match_parent"
@@ -33,15 +50,35 @@
<FrameLayout
android:id="@+id/container_save"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:background="@*android:color/material_grey_50"
+ android:elevation="8dp" />
</LinearLayout>
- <FrameLayout
- android:id="@+id/container_roots"
- android:layout_width="250dp"
+ <LinearLayout
+ android:id="@+id/drawer_roots"
+ android:layout_width="256dp"
android:layout_height="match_parent"
android:layout_gravity="start"
- android:background="#fff" />
+ android:orientation="vertical"
+ android:elevation="16dp"
+ android:background="@*android:color/white">
+
+ <Toolbar
+ android:id="@+id/roots_toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:background="?android:attr/colorPrimary"
+ android:elevation="8dp"
+ android:theme="?android:attr/actionBarTheme" />
+
+ <FrameLayout
+ android:id="@+id/container_roots"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ </LinearLayout>
</android.support.v4.widget.DrawerLayout>
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index 09b50c0..ffbd3f0 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -17,7 +17,7 @@
<com.android.documentsui.DirectoryView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/ic_dir_shadow">
+ android:background="@*android:color/material_grey_50">
<TextView
android:id="@android:id/empty"
diff --git a/packages/DocumentsUI/res/layout/fragment_pick.xml b/packages/DocumentsUI/res/layout/fragment_pick.xml
index 4a2fd03..5735871 100644
--- a/packages/DocumentsUI/res/layout/fragment_pick.xml
+++ b/packages/DocumentsUI/res/layout/fragment_pick.xml
@@ -17,32 +17,16 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:orientation="horizontal"
+ android:baselineAligned="false"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall">
- <!-- Le sigh, this really should be an asset -->
- <View
+ <Button
+ android:id="@android:id/button1"
android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="#ccc" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:baselineAligned="false"
- android:gravity="center_vertical"
- android:background="#ddd"
- android:minHeight="?android:attr/listPreferredItemHeightSmall">
-
- <Button
- android:id="@android:id/button1"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="?android:attr/selectableItemBackground"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAllCaps="false"
- android:padding="8dp" />
-
- </LinearLayout>
+ android:layout_height="match_parent"
+ android:textAllCaps="false"
+ style="?android:attr/buttonBarPositiveButtonStyle" />
</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/fragment_roots.xml b/packages/DocumentsUI/res/layout/fragment_roots.xml
index c3a3da0..2d624d8 100644
--- a/packages/DocumentsUI/res/layout/fragment_roots.xml
+++ b/packages/DocumentsUI/res/layout/fragment_roots.xml
@@ -18,4 +18,5 @@
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:divider="@drawable/ic_drawer_hairline_divider" />
+ android:paddingTop="8dp"
+ android:divider="@null" />
diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml
index d601194..7aac620 100644
--- a/packages/DocumentsUI/res/layout/fragment_save.xml
+++ b/packages/DocumentsUI/res/layout/fragment_save.xml
@@ -17,66 +17,55 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:paddingStart="@dimen/list_item_padding"
+ android:orientation="horizontal"
+ android:baselineAligned="false"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall">
- <!-- Le sigh, this really should be an asset -->
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="#ccc" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:baselineAligned="false"
- android:gravity="center_vertical"
- android:background="#ddd"
- android:minHeight="?android:attr/listPreferredItemHeightSmall">
+ <FrameLayout
+ android:layout_width="@dimen/icon_size"
+ android:layout_height="@dimen/icon_size"
+ android:layout_marginEnd="16dp">
<ImageView
android:id="@android:id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
+ android:layout_width="@dimen/root_icon_size"
+ android:layout_height="match_parent"
android:scaleType="centerInside"
android:contentDescription="@null" />
- <EditText
- android:id="@android:id/title"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:singleLine="true"
- android:selectAllOnFocus="true" />
+ </FrameLayout>
- <FrameLayout
+ <EditText
+ android:id="@android:id/title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:singleLine="true"
+ android:selectAllOnFocus="true" />
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@android:id/button1"
android:layout_width="wrap_content"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:text="@string/menu_save"
+ style="?android:attr/buttonBarPositiveButtonStyle" />
- <Button
- android:id="@android:id/button1"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/menu_save"
- android:textAllCaps="true"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:padding="8dp" />
+ <ProgressBar
+ android:id="@android:id/progress"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:visibility="gone"
+ android:indeterminate="true"
+ android:padding="8dp"
+ style="?android:attr/progressBarStyle" />
- <ProgressBar
- android:id="@android:id/progress"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:visibility="gone"
- android:indeterminate="true"
- android:padding="8dp"
- style="?android:attr/progressBarStyle" />
-
- </FrameLayout>
-
- </LinearLayout>
+ </FrameLayout>
</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml
index 0fc606d..bdb3184 100644
--- a/packages/DocumentsUI/res/layout/item_doc_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml
@@ -19,7 +19,7 @@
android:layout_height="@dimen/grid_item_height"
android:orientation="vertical"
android:background="@color/grid_item_background"
- android:foreground="@drawable/item_activated">
+ android:foreground="@drawable/item_activated_overlay">
<ImageView
android:id="@+id/icon_thumb"
@@ -46,8 +46,8 @@
android:layout_height="wrap_content"
android:background="@drawable/grid_protect_background"
android:orientation="vertical"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingStart="16dp"
+ android:paddingEnd="12dp"
android:paddingTop="8dp"
android:paddingBottom="8dp">
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index 50ed2d6..c5f1842 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -17,7 +17,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:foreground="@drawable/item_activated">
+ android:background="@drawable/item_activated">
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/res/layout/item_root.xml b/packages/DocumentsUI/res/layout/item_root.xml
index f17c261..266b9b0 100644
--- a/packages/DocumentsUI/res/layout/item_root.xml
+++ b/packages/DocumentsUI/res/layout/item_root.xml
@@ -17,25 +17,33 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:minHeight="48dp"
+ android:paddingStart="@dimen/list_item_padding"
+ android:paddingEnd="@dimen/list_item_padding"
android:gravity="center_vertical"
android:orientation="horizontal"
android:baselineAligned="false"
- android:background="@drawable/item_root">
+ android:background="@drawable/item_activated">
- <ImageView
- android:id="@android:id/icon"
+ <FrameLayout
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
- android:layout_marginEnd="8dp"
- android:scaleType="centerInside"
- android:contentDescription="@null" />
+ android:layout_marginEnd="16dp">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/root_icon_size"
+ android:layout_height="match_parent"
+ android:scaleType="centerInside"
+ android:contentDescription="@null" />
+
+ </FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
android:orientation="vertical">
<TextView
@@ -45,7 +53,8 @@
android:singleLine="true"
android:ellipsize="end"
android:textAlignment="viewStart"
- style="@style/TextAppearance.Medium" />
+ android:textAppearance="@android:style/TextAppearance.Material.Body1"
+ android:textColor="?android:attr/textColorPrimary" />
<TextView
android:id="@android:id/summary"
@@ -54,7 +63,8 @@
android:singleLine="true"
android:ellipsize="end"
android:textAlignment="viewStart"
- style="@style/TextAppearance.Small" />
+ android:textAppearance="@android:style/TextAppearance.Material.Body1"
+ android:textColor="?android:attr/textColorSecondary" />
</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/item_root_spacer.xml b/packages/DocumentsUI/res/layout/item_root_spacer.xml
index 7d96ac8..b3beced 100644
--- a/packages/DocumentsUI/res/layout/item_root_spacer.xml
+++ b/packages/DocumentsUI/res/layout/item_root_spacer.xml
@@ -14,7 +14,15 @@
limitations under the License.
-->
-<View xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/ic_drawer_tall_divider" />
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/listDivider" />
+
+</FrameLayout>
diff --git a/packages/DocumentsUI/res/layout/item_title.xml b/packages/DocumentsUI/res/layout/item_subdir.xml
similarity index 87%
rename from packages/DocumentsUI/res/layout/item_title.xml
rename to packages/DocumentsUI/res/layout/item_subdir.xml
index 6e96fb5..b2a739a 100644
--- a/packages/DocumentsUI/res/layout/item_title.xml
+++ b/packages/DocumentsUI/res/layout/item_subdir.xml
@@ -28,6 +28,7 @@
android:id="@+id/subdir"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:paddingEnd="8dp"
android:scaleType="centerInside"
android:visibility="gone"
android:src="@drawable/ic_subdirectory_arrow"
@@ -35,11 +36,13 @@
<TextView
android:id="@android:id/title"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_weight="1"
android:singleLine="true"
android:ellipsize="middle"
android:textAlignment="viewStart"
- style="@style/TextAppearance.Medium" />
+ android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+ android:textColor="?android:attr/textColorPrimary" />
</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/item_title.xml b/packages/DocumentsUI/res/layout/item_subdir_title.xml
similarity index 63%
copy from packages/DocumentsUI/res/layout/item_title.xml
copy to packages/DocumentsUI/res/layout/item_subdir_title.xml
index 6e96fb5..4c839d0 100644
--- a/packages/DocumentsUI/res/layout/item_title.xml
+++ b/packages/DocumentsUI/res/layout/item_subdir_title.xml
@@ -15,31 +15,19 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:gravity="center_vertical"
+ android:paddingEnd="8dp"
android:orientation="horizontal"
android:baselineAligned="false">
- <ImageView
- android:id="@+id/subdir"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scaleType="centerInside"
- android:visibility="gone"
- android:src="@drawable/ic_subdirectory_arrow"
- android:contentDescription="@null" />
-
<TextView
android:id="@android:id/title"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="middle"
android:textAlignment="viewStart"
- style="@style/TextAppearance.Medium" />
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title" />
</LinearLayout>
diff --git a/packages/DocumentsUI/res/values-sw720dp/dimens.xml b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
index 75afe01..068c806 100644
--- a/packages/DocumentsUI/res/values-sw720dp/dimens.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
@@ -21,7 +21,7 @@
<item type="dimen" name="dialog_height">90%</item>
<dimen name="grid_padding_horiz">24dp</dimen>
- <dimen name="grid_padding_vert">8dp</dimen>
+ <dimen name="grid_padding_vert">16dp</dimen>
<dimen name="grid_item_padding">8dp</dimen>
diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml
deleted file mode 100644
index 8d31444..0000000
--- a/packages/DocumentsUI/res/values-sw720dp/styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="Theme" parent="@android:style/Theme.DeviceDefault.Light">
- <item name="android:actionOverflowButtonStyle">@style/DarkerOverflow</item>
- <item name="android:windowBackground">@*android:drawable/dialog_full_holo_light</item>
- <item name="android:colorBackgroundCacheHint">@null</item>
- <item name="android:windowIsTranslucent">true</item>
- <item name="android:windowAnimationStyle">@*android:style/Animation.DeviceDefault.Dialog</item>
- </style>
-</resources>
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index e3d7f2d..08159c4 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -15,12 +15,10 @@
-->
<resources>
- <color name="chip">#ddd</color>
- <color name="item_root_pressed">#33cccccc</color>
- <color name="item_root_focused">#66cccccc</color>
- <color name="grid_item_background">#ffe1e1e0</color>
+ <!-- Half-alpha of material_teal_500 -->
+ <color name="accent_color_overlay">#8800bcd4</color>
- <color name="accent_item_activated">#88009587</color>
+ <color name="grid_item_background">@*android:color/material_grey_300</color>
</resources>
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
index 83a0bf4..d4d7ede 100644
--- a/packages/DocumentsUI/res/values/dimens.xml
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -37,4 +37,6 @@
<bool name="show_as_dialog">false</bool>
<bool name="always_show_summary">false</bool>
+ <dimen name="dir_elevation">8dp</dimen>
+
</resources>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index a416eb4..4bd6991 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -15,6 +15,29 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="DocumentsTheme" parent="@android:style/Theme.DeviceDefault.Light.DialogWhenLarge">
+ <item name="android:actionBarWidgetTheme">@null</item>
+ <item name="android:actionBarTheme">@*android:style/ThemeOverlay.Material.Dark.ActionBar</item>
+ <item name="android:actionBarPopupTheme">@*android:style/ThemeOverlay.Material.Light</item>
+
+ <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_900</item>
+ <item name="android:colorPrimary">@*android:color/material_blue_grey_800</item>
+ <item name="android:colorAccent">@*android:color/material_teal_500</item>
+
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowNoTitle">true</item>
+
+ <item name="*android:windowFixedWidthMajor">@null</item>
+ <item name="*android:windowFixedWidthMinor">@null</item>
+ <item name="*android:windowMinWidthMajor">@null</item>
+ <item name="*android:windowMinWidthMinor">@null</item>
+ <item name="*android:windowFixedHeightMajor">80%</item>
+ <item name="*android:windowFixedHeightMinor">90%</item>
+
+ <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
+ </style>
+
<style name="TextAppearance" />
<style name="TextAppearance.Medium">
@@ -27,14 +50,4 @@
<item name="android:textColor">?android:attr/textColorTertiary</item>
</style>
- <!-- Normally just a redirection, but this is used to make ourselves a
- dialog on large tablets -->
- <style name="Theme" parent="@android:style/Theme.DeviceDefault.Light">
- <item name="android:actionOverflowButtonStyle">@style/DarkerOverflow</item>
- </style>
-
- <style name="DarkerOverflow" parent="@android:style/Widget.DeviceDefault.Light.ActionButton.Overflow">
- <item name="android:src">@drawable/ic_menu_overflow</item>
- </style>
-
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryContainerView.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryContainerView.java
index 00b3c87..71ea8a9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryContainerView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryContainerView.java
@@ -36,14 +36,6 @@
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- setClipChildren(false);
- setClipToOutline(false);
- setClipToPadding(false);
- }
-
- @Override
protected void dispatchDraw(Canvas canvas) {
final ArrayList<View> disappearing = mDisappearingChildren;
if (mDisappearingFirst && disappearing != null) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index e013cc3..001cac46 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -853,7 +853,11 @@
// We've already had to enumerate roots before any results can
// be shown, so this will never block.
final RootInfo root = roots.getRootBlocking(docAuthority, docRootId);
- iconDrawable = root.loadIcon(context);
+ if (state.derivedMode == MODE_GRID) {
+ iconDrawable = root.loadLightIcon(context);
+ } else {
+ iconDrawable = root.loadIcon(context);
+ }
if (summary != null) {
final boolean alwaysShowSummary = getResources()
@@ -880,7 +884,7 @@
if (Document.MIME_TYPE_DIR.equals(docMimeType) && state.derivedMode == MODE_GRID
&& showThumbnail) {
iconDrawable = context.getResources().getDrawable(
- R.drawable.ic_root_folder_dark);
+ R.drawable.ic_root_folder_light);
}
if (summary != null) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
index c163c46..4f52a03d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
@@ -37,19 +37,6 @@
}
@Override
- public void setBackground(Drawable background) {
- final Rect rect = new Rect();
- background.getPadding(rect);
-
- final boolean insetLeft = getResources().getBoolean(R.bool.list_divider_inset_left);
- if (insetLeft) {
- super.setBackground(new InsetDrawable(background, -rect.left, 0, -rect.right, 0));
- } else {
- super.setBackground(new InsetDrawable(background, -rect.right, 0, -rect.left, 0));
- }
- }
-
- @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
@@ -63,5 +50,11 @@
public void setPosition(float position) {
mPosition = position;
setX((mWidth > 0) ? (mPosition * mWidth) : 0);
+
+ if (mPosition != 0) {
+ setTranslationZ(getResources().getDimensionPixelSize(R.dimen.dir_elevation));
+ } else {
+ setTranslationZ(0);
+ }
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index d0b6a1d..a2e8432 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -28,8 +28,6 @@
import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
-import android.app.ActionBar;
-import android.app.ActionBar.OnNavigationListener;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
@@ -39,14 +37,12 @@
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Point;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -55,7 +51,6 @@
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
import android.support.v4.app.ActionBarDrawerToggle;
-import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.DrawerLayout.DrawerListener;
import android.util.Log;
@@ -64,17 +59,19 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnActionExpandListener;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
+import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
+import android.widget.Toolbar;
import com.android.documentsui.RecentsProvider.RecentColumns;
import com.android.documentsui.RecentsProvider.ResumeColumns;
@@ -105,9 +102,14 @@
private SearchView mSearchView;
+ private Toolbar mToolbar;
+ private Spinner mToolbarStack;
+
+ private Toolbar mRootsToolbar;
+
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle mDrawerToggle;
- private View mRootsContainer;
+ private View mRootsDrawer;
private DirectoryContainerView mDirectoryContainer;
@@ -115,6 +117,8 @@
private boolean mIgnoreNextClose;
private boolean mIgnoreNextCollapse;
+ private boolean mSearchExpanded;
+
private RootsCache mRoots;
private State mState;
@@ -127,59 +131,31 @@
setResult(Activity.RESULT_CANCELED);
setContentView(R.layout.activity);
+ final Context context = this;
final Resources res = getResources();
mShowAsDialog = res.getBoolean(R.bool.show_as_dialog);
if (mShowAsDialog) {
- // backgroundDimAmount from theme isn't applied; do it manually
+ // Strongly define our horizontal dimension; we leave vertical as
+ // WRAP_CONTENT so that system resizes us when IME is showing.
final WindowManager.LayoutParams a = getWindow().getAttributes();
- a.dimAmount = 0.6f;
- getWindow().setAttributes(a);
- getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
- getWindow().setFlags(~0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-
- // Inset ourselves to look like a dialog
final Point size = new Point();
getWindowManager().getDefaultDisplay().getSize(size);
+ a.width = (int) res.getFraction(R.dimen.dialog_width, size.x, size.x);
- final int width = (int) res.getFraction(R.dimen.dialog_width, size.x, size.x);
- final int height = (int) res.getFraction(R.dimen.dialog_height, size.y, size.y);
- final int insetX = (size.x - width) / 2;
- final int insetY = (size.y - height) / 2;
-
- final Drawable before = getWindow().getDecorView().getBackground();
- final Drawable after = new InsetDrawable(before, insetX, insetY, insetX, insetY);
- getWindow().getDecorView().setBackground(after);
-
- // Dismiss when touch down in the dimmed inset area
- getWindow().getDecorView().setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- final float x = event.getX();
- final float y = event.getY();
- if (x < insetX || x > v.getWidth() - insetX || y < insetY
- || y > v.getHeight() - insetY) {
- finish();
- return true;
- }
- }
- return false;
- }
- });
+ getWindow().setAttributes(a);
} else {
// Non-dialog means we have a drawer
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
- R.drawable.ic_drawer_glyph, R.string.drawer_open, R.string.drawer_close);
+ R.drawable.ic_hamburger, R.string.drawer_open, R.string.drawer_close);
mDrawerLayout.setDrawerListener(mDrawerListener);
- mDrawerLayout.setDrawerShadow(R.drawable.ic_drawer_shadow, GravityCompat.START);
- mRootsContainer = findViewById(R.id.container_roots);
+ mRootsDrawer = findViewById(R.id.drawer_roots);
}
mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
@@ -190,10 +166,25 @@
buildDefaultState();
}
+ mToolbar = (Toolbar) findViewById(R.id.toolbar);
+ mToolbar.setTitleTextAppearance(context,
+ android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
+
+ mToolbarStack = (Spinner) findViewById(R.id.stack);
+ mToolbarStack.setOnItemSelectedListener(mStackListener);
+
+ mRootsToolbar = (Toolbar) findViewById(R.id.roots_toolbar);
+ if (mRootsToolbar != null) {
+ mRootsToolbar.setTitleTextAppearance(context,
+ android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
+ }
+
+ setActionBar(mToolbar);
+
// Hide roots when we're managing a specific root
if (mState.action == ACTION_MANAGE) {
if (mShowAsDialog) {
- findViewById(R.id.dialog_roots).setVisibility(View.GONE);
+ findViewById(R.id.container_roots).setVisibility(View.GONE);
} else {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
@@ -382,15 +373,11 @@
@Override
public void onDrawerOpened(View drawerView) {
mDrawerToggle.onDrawerOpened(drawerView);
- updateActionBar();
- invalidateOptionsMenu();
}
@Override
public void onDrawerClosed(View drawerView) {
mDrawerToggle.onDrawerClosed(drawerView);
- updateActionBar();
- invalidateOptionsMenu();
}
@Override
@@ -410,9 +397,9 @@
public void setRootsDrawerOpen(boolean open) {
if (!mShowAsDialog) {
if (open) {
- mDrawerLayout.openDrawer(mRootsContainer);
+ mDrawerLayout.openDrawer(mRootsDrawer);
} else {
- mDrawerLayout.closeDrawer(mRootsContainer);
+ mDrawerLayout.closeDrawer(mRootsDrawer);
}
}
}
@@ -421,44 +408,51 @@
if (mShowAsDialog) {
return false;
} else {
- return mDrawerLayout.isDrawerOpen(mRootsContainer);
+ return mDrawerLayout.isDrawerOpen(mRootsDrawer);
}
}
public void updateActionBar() {
- final ActionBar actionBar = getActionBar();
-
- actionBar.setDisplayShowHomeEnabled(true);
-
- final boolean showIndicator = !mShowAsDialog && (mState.action != ACTION_MANAGE);
- actionBar.setDisplayHomeAsUpEnabled(showIndicator);
- if (mDrawerToggle != null) {
- mDrawerToggle.setDrawerIndicatorEnabled(showIndicator);
- }
-
- if (isRootsDrawerOpen()) {
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
- actionBar.setIcon(new ColorDrawable());
-
+ if (mRootsToolbar != null) {
if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT
|| mState.action == ACTION_OPEN_TREE) {
- actionBar.setTitle(R.string.title_open);
+ mRootsToolbar.setTitle(R.string.title_open);
} else if (mState.action == ACTION_CREATE) {
- actionBar.setTitle(R.string.title_save);
+ mRootsToolbar.setTitle(R.string.title_save);
}
- } else {
- final RootInfo root = getCurrentRoot();
- actionBar.setIcon(root != null ? root.loadIcon(this) : null);
+ }
+ final RootInfo root = getCurrentRoot();
+ final boolean showRootIcon = mShowAsDialog || (mState.action == ACTION_MANAGE);
+ if (showRootIcon) {
+ mToolbar.setNavigationIcon(root != null ? root.loadLightIcon(this) : null);
+ mToolbar.setNavigationOnClickListener(null);
+ } else {
+ mToolbar.setNavigationIcon(R.drawable.ic_hamburger);
+ mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setRootsDrawerOpen(true);
+ }
+ });
+ }
+
+ if (mSearchExpanded) {
+ mToolbar.setTitle(null);
+ mToolbarStack.setVisibility(View.GONE);
+ mToolbarStack.setAdapter(null);
+ } else {
if (mState.stack.size() <= 1) {
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
- actionBar.setTitle(root.title);
+ mToolbar.setTitle(root.title);
+ mToolbarStack.setVisibility(View.GONE);
+ mToolbarStack.setAdapter(null);
} else {
+ mToolbar.setTitle(null);
+ mToolbarStack.setVisibility(View.VISIBLE);
+ mToolbarStack.setAdapter(mStackAdapter);
+
mIgnoreNextNavigation = true;
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
- actionBar.setTitle(null);
- actionBar.setListNavigationCallbacks(mStackAdapter, mStackListener);
- actionBar.setSelectedNavigationItem(mStackAdapter.getCount() - 1);
+ mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
}
}
}
@@ -480,6 +474,7 @@
mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
+ mSearchExpanded = true;
mState.currentSearch = query;
mSearchView.clearFocus();
onCurrentDirectoryChanged(ANIM_NONE);
@@ -495,11 +490,14 @@
searchMenu.setOnActionExpandListener(new OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
+ mSearchExpanded = true;
+ updateActionBar();
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
+ mSearchExpanded = false;
if (mIgnoreNextCollapse) {
mIgnoreNextCollapse = false;
return true;
@@ -514,6 +512,7 @@
mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
@Override
public boolean onClose() {
+ mSearchExpanded = false;
if (mIgnoreNextClose) {
mIgnoreNextClose = false;
return false;
@@ -545,18 +544,6 @@
final MenuItem list = menu.findItem(R.id.menu_list);
final MenuItem settings = menu.findItem(R.id.menu_settings);
- // Open drawer means we hide most actions
- if (isRootsDrawerOpen()) {
- createDir.setVisible(false);
- search.setVisible(false);
- sort.setVisible(false);
- grid.setVisible(false);
- list.setVisible(false);
- mIgnoreNextCollapse = true;
- search.collapseActionView();
- return true;
- }
-
sort.setVisible(cwd != null);
grid.setVisible(mState.derivedMode != MODE_GRID);
list.setVisible(mState.derivedMode != MODE_LIST);
@@ -730,7 +717,7 @@
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.item_title, parent, false);
+ .inflate(R.layout.item_subdir_title, parent, false);
}
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
@@ -743,8 +730,6 @@
title.setText(doc.displayName);
}
- // No padding when shown in actionbar
- convertView.setPadding(0, 0, 0, 0);
return convertView;
}
@@ -752,7 +737,7 @@
public View getDropDownView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.item_title, parent, false);
+ .inflate(R.layout.item_subdir, parent, false);
}
final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
@@ -772,20 +757,24 @@
}
};
- private OnNavigationListener mStackListener = new OnNavigationListener() {
+ private OnItemSelectedListener mStackListener = new OnItemSelectedListener() {
@Override
- public boolean onNavigationItemSelected(int itemPosition, long itemId) {
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (mIgnoreNextNavigation) {
mIgnoreNextNavigation = false;
- return false;
+ return;
}
- while (mState.stack.size() > itemPosition + 1) {
+ while (mState.stack.size() > position + 1) {
mState.stackTouched = true;
mState.stack.pop();
}
onCurrentDirectoryChanged(ANIM_UP);
- return true;
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Ignored
}
};
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index d06e858..1235c9b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -104,6 +104,7 @@
mRecentsRoot.authority = null;
mRecentsRoot.rootId = null;
mRecentsRoot.icon = R.drawable.ic_root_recent_dark;
+ mRecentsRoot.lightIcon = R.drawable.ic_root_recent_light;
mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE
| Root.FLAG_SUPPORTS_IS_CHILD;
mRecentsRoot.title = mContext.getString(R.string.root_recent);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index efa7785..79cbf26 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -48,6 +48,7 @@
public String rootId;
public int flags;
public int icon;
+ public int lightIcon;
public String title;
public String summary;
public String documentId;
@@ -58,6 +59,7 @@
public String derivedPackageName;
public String[] derivedMimeTypes;
public int derivedIcon;
+ public int derivedLightIcon;
public RootInfo() {
reset();
@@ -69,6 +71,7 @@
rootId = null;
flags = 0;
icon = 0;
+ lightIcon = 0;
title = null;
summary = null;
documentId = null;
@@ -78,6 +81,7 @@
derivedPackageName = null;
derivedMimeTypes = null;
derivedIcon = 0;
+ derivedLightIcon = 0;
}
@Override
@@ -160,14 +164,19 @@
// TODO: remove these special case icons
if (isExternalStorage()) {
derivedIcon = R.drawable.ic_root_sdcard_dark;
+ derivedLightIcon = R.drawable.ic_root_sdcard_light;
} else if (isDownloads()) {
derivedIcon = R.drawable.ic_root_download_dark;
+ derivedLightIcon = R.drawable.ic_root_download_light;
} else if (isImages()) {
derivedIcon = R.drawable.ic_doc_image_dark;
+ derivedLightIcon = R.drawable.ic_doc_image_light;
} else if (isVideos()) {
derivedIcon = R.drawable.ic_doc_video_dark;
+ derivedLightIcon = R.drawable.ic_doc_video_light;
} else if (isAudio()) {
derivedIcon = R.drawable.ic_doc_audio_dark;
+ derivedLightIcon = R.drawable.ic_doc_audio_light;
}
}
@@ -211,6 +220,16 @@
}
}
+ public Drawable loadLightIcon(Context context) {
+ if (derivedLightIcon != 0) {
+ return context.getResources().getDrawable(derivedLightIcon);
+ } else if (lightIcon != 0) {
+ return IconUtils.loadPackageIcon(context, authority, lightIcon);
+ } else {
+ return loadIcon(context);
+ }
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof RootInfo) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index b80a33b..1932843 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -937,12 +937,16 @@
}
private void updateNotificationTranslucency() {
float alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
- / (mQsMinExpansionHeight + mNotificationStackScroller.getItemHeight() / 2);
+ / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
+ + mNotificationStackScroller.getCollapseSecondCardPadding());
alpha = Math.max(0, Math.min(alpha, 1));
alpha = (float) Math.pow(alpha, 0.75);
-
- // TODO: Draw a rect with DST_OUT over the notifications to achieve the same effect -
- // this would be much more efficient.
+ if (alpha != 1f && mNotificationStackScroller.getLayerType() != LAYER_TYPE_HARDWARE) {
+ mNotificationStackScroller.setLayerType(LAYER_TYPE_HARDWARE, null);
+ } else if (alpha == 1f
+ && mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) {
+ mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null);
+ }
mNotificationStackScroller.setAlpha(alpha);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 9b819f2b..e0167e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -470,6 +470,10 @@
return mBottomStackPeekSize;
}
+ public int getCollapseSecondCardPadding() {
+ return mCollapseSecondCardPadding;
+ }
+
public void setLongPressListener(SwipeHelper.LongPressListener listener) {
mSwipeHelper.setLongPressListener(listener);
mLongPressListener = listener;
@@ -1955,8 +1959,10 @@
}
public void setScrimAlpha(float progress) {
- mAmbientState.setScrimAmount(progress);
- requestChildrenUpdate();
+ if (progress != mAmbientState.getScrimAmount()) {
+ mAmbientState.setScrimAmount(progress);
+ requestChildrenUpdate();
+ }
}
/**
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
new file mode 100644
index 0000000..616b670
--- /dev/null
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2014 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;
+
+import com.android.internal.telephony.IMms;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+
+/**
+ * This class is a proxy for MmsService APIs. We need this because MmsService runs
+ * in phone process and may crash anytime. This manages a connection to the actual
+ * MmsService and bridges the public SMS/MMS APIs with MmsService implementation.
+ */
+public class MmsServiceBroker extends SystemService {
+ private static final String TAG = "MmsServiceBroker";
+
+ private static final ComponentName MMS_SERVICE_COMPONENT =
+ new ComponentName("com.android.mms.service", "com.android.mms.service.MmsService");
+
+ private static final int MSG_TRY_CONNECTING = 1;
+
+ private Context mContext;
+ // The actual MMS service instance to invoke
+ private volatile IMms mService;
+ private boolean mIsConnecting;
+
+ // Cached system service instances
+ private volatile AppOpsManager mAppOpsManager = null;
+ private volatile PackageManager mPackageManager = null;
+ private volatile TelephonyManager mTelephonyManager = null;
+
+ private final Handler mConnectionHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_TRY_CONNECTING:
+ tryConnecting();
+ break;
+ default:
+ Slog.e(TAG, "Unknown message");
+ }
+ }
+ };
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Slog.i(TAG, "MmsService connected");
+ synchronized (MmsServiceBroker.this) {
+ mService = IMms.Stub.asInterface(service);
+ mIsConnecting = false;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Slog.i(TAG, "MmsService unexpectedly disconnected");
+ synchronized (MmsServiceBroker.this) {
+ mService = null;
+ mIsConnecting = false;
+ }
+ }
+ };
+
+ public MmsServiceBroker(Context context) {
+ super(context);
+ mContext = context;
+ mService = null;
+ mIsConnecting = false;
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService("imms", new BinderService());
+ }
+
+ public void systemRunning() {
+ tryConnecting();
+ }
+
+ private void tryConnecting() {
+ Slog.i(TAG, "Connecting to MmsService");
+ synchronized (this) {
+ if (mIsConnecting) {
+ Slog.d(TAG, "Already connecting");
+ return;
+ }
+ final Intent intent = new Intent();
+ intent.setComponent(MMS_SERVICE_COMPONENT);
+ try {
+ if (mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+ mIsConnecting = true;
+ } else {
+ Slog.e(TAG, "Failed to connect to MmsService");
+ }
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Forbidden to connect to MmsService", e);
+ }
+ }
+ }
+
+ private void ensureService() {
+ if (mService == null) {
+ // Service instance lost, kicking off the connection again
+ mConnectionHandler.sendMessage(mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
+ throw new RuntimeException("MMS service is not connected");
+ }
+ }
+
+ /**
+ * Making sure when we obtain the mService instance it is always valid.
+ * Throws {@link RuntimeException} when it is empty.
+ */
+ private IMms getServiceGuarded() {
+ ensureService();
+ return mService;
+ }
+
+ private AppOpsManager getAppOpsManager() {
+ if (mAppOpsManager == null) {
+ mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ }
+ return mAppOpsManager;
+ }
+
+ private PackageManager getPackageManager() {
+ if (mPackageManager == null) {
+ mPackageManager = mContext.getPackageManager();
+ }
+ return mPackageManager;
+ }
+
+ private TelephonyManager getTelephonyManager() {
+ if (mTelephonyManager == null) {
+ mTelephonyManager = (TelephonyManager) mContext.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ }
+ return mTelephonyManager;
+ }
+
+ /*
+ * Throws a security exception unless the caller has carrier privilege.
+ */
+ private void enforceCarrierPrivilege() {
+ String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
+ for (String pkg : packages) {
+ if (getTelephonyManager().checkCarrierPrivilegesForPackage(pkg) ==
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return;
+ }
+ }
+ throw new SecurityException("No carrier privilege");
+ }
+
+ // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service"
+ private final class BinderService extends IMms.Stub {
+ @Override
+ public void sendMessage(long subId, String callingPkg, byte[] pdu, String locationUrl,
+ PendingIntent sentIntent) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+ getServiceGuarded().sendMessage(subId, callingPkg, pdu, locationUrl, sentIntent);
+ }
+
+ @Override
+ public void downloadMessage(long subId, String callingPkg, String locationUrl,
+ PendingIntent downloadedIntent) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
+ "Download MMS message");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+ getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, downloadedIntent);
+ }
+
+ @Override
+ public void updateMmsSendStatus(int messageRef, boolean success) throws RemoteException {
+ enforceCarrierPrivilege();
+ getServiceGuarded().updateMmsSendStatus(messageRef, success);
+ }
+
+ @Override
+ public void updateMmsDownloadStatus(int messageRef, byte[] pdu) throws RemoteException {
+ enforceCarrierPrivilege();
+ getServiceGuarded().updateMmsDownloadStatus(messageRef, pdu);
+ }
+
+ @Override
+ public boolean getCarrierConfigBoolean(String name, boolean defaultValue)
+ throws RemoteException {
+ return getServiceGuarded().getCarrierConfigBoolean(name, defaultValue);
+ }
+
+ @Override
+ public int getCarrierConfigInt(String name, int defaultValue) throws RemoteException {
+ return getServiceGuarded().getCarrierConfigInt(name, defaultValue);
+ }
+
+ @Override
+ public String getCarrierConfigString(String name, String defaultValue)
+ throws RemoteException {
+ return getServiceGuarded().getCarrierConfigString(name, defaultValue);
+ }
+
+ @Override
+ public void setCarrierConfigBoolean(String callingPkg, String name, boolean value)
+ throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+ getServiceGuarded().setCarrierConfigBoolean(callingPkg, name, value);
+ }
+
+ @Override
+ public void setCarrierConfigInt(String callingPkg, String name, int value)
+ throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+ getServiceGuarded().setCarrierConfigInt(callingPkg, name, value);
+ }
+
+ @Override
+ public void setCarrierConfigString(String callingPkg, String name, String value)
+ throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+ getServiceGuarded().setCarrierConfigString(callingPkg, name, value);
+ }
+
+ @Override
+ public Uri importTextMessage(String callingPkg, String address, int type, String text,
+ long timestampMillis, boolean seen, boolean read) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import SMS message");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return null;
+ }
+ return getServiceGuarded().importTextMessage(
+ callingPkg, address, type, text, timestampMillis, seen, read);
+ }
+
+ @Override
+ public Uri importMultimediaMessage(String callingPkg, byte[] pdu, String messageId,
+ long timestampSecs, boolean seen, boolean read) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import MMS message");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return null;
+ }
+ return getServiceGuarded().importMultimediaMessage(
+ callingPkg, pdu, messageId, timestampSecs, seen, read);
+ }
+
+ @Override
+ public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
+ throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
+ "Delete SMS/MMS message");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri);
+ }
+
+ @Override
+ public boolean deleteStoredConversation(String callingPkg, long conversationId)
+ throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Delete conversation");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId);
+ }
+
+ @Override
+ public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
+ ContentValues statusValues) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
+ "Update SMS/MMS message");
+ return getServiceGuarded()
+ .updateStoredMessageStatus(callingPkg, messageUri, statusValues);
+ }
+
+ @Override
+ public Uri addTextMessageDraft(String callingPkg, String address, String text)
+ throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add SMS draft");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return null;
+ }
+ return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
+ }
+
+ @Override
+ public Uri addMultimediaMessageDraft(String callingPkg, byte[] pdu) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add MMS draft");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return null;
+ }
+ return getServiceGuarded().addMultimediaMessageDraft(callingPkg, pdu);
+ }
+
+ @Override
+ public void sendStoredMessage(long subId, String callingPkg, Uri messageUri,
+ PendingIntent sentIntent) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.SEND_SMS,
+ "Send stored MMS message");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+ getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, sentIntent);
+ }
+
+ @Override
+ public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Set auto persist");
+ if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
+ callingPkg) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+ getServiceGuarded().setAutoPersisting(callingPkg, enabled);
+ }
+
+ @Override
+ public boolean getAutoPersisting() throws RemoteException {
+ return getServiceGuarded().getAutoPersisting();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 538501b..3e2f260 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -27,6 +27,7 @@
import android.net.NetworkScorerAppManager;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
import android.net.ScoredNetwork;
+import android.os.Binder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
@@ -131,17 +132,44 @@
@Override
public boolean setActiveScorer(String packageName) {
mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
- // Preemptively clear scores even though the set operation could fail. We do this for safety
- // as scores should never be compared across apps; in practice, Settings should only be
- // allowing valid apps to be set as scorers, so failure here should be rare.
- clearInternal();
- boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
- if (result) {
- Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
- intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
- mContext.sendBroadcast(intent);
+ return setScorerInternal(packageName);
+ }
+
+ @Override
+ public void disableScoring() {
+ // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETOWRKS) should
+ // be allowed to disable scoring.
+ if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
+ mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
+ PackageManager.PERMISSION_GRANTED) {
+ // The return value is discarded here because at this point, the call should always
+ // succeed. The only reason for failure is if the new package is not a valid scorer, but
+ // we're disabling scoring altogether here.
+ setScorerInternal(null /* packageName */);
+ } else {
+ throw new SecurityException(
+ "Caller is neither the active scorer nor the scorer manager.");
}
- return result;
+ }
+
+ /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */
+ private boolean setScorerInternal(String packageName) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ // Preemptively clear scores even though the set operation could fail. We do this for
+ // safety as scores should never be compared across apps; in practice, Settings should
+ // only be allowing valid apps to be set as scorers, so failure here should be rare.
+ clearInternal();
+ boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
+ if (result) {
+ Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
+ intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
+ mContext.sendBroadcast(intent);
+ }
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/** Clear scores. Callers are responsible for checking permissions as appropriate. */
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d7b2a98..cfdf7cf 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3698,7 +3698,7 @@
// specified, then replace it with the existing recent task.
task = tr;
}
- mTaskPersister.notify(tr, false);
+ notifyTaskPersisterLocked(tr, false);
}
if (N >= MAX_RECENT_TASKS) {
final TaskRecord tr = mRecentTasks.remove(N - 1);
@@ -7646,7 +7646,7 @@
tr.removeTaskActivitiesLocked();
cleanUpRemovedTaskLocked(tr, flags);
if (tr.isPersistable) {
- notifyTaskPersisterLocked(tr, true);
+ notifyTaskPersisterLocked(null, true);
}
return true;
}
@@ -9089,7 +9089,11 @@
}
void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
- mTaskPersister.notify(task, flush);
+ if (task != null && task.stack != null && task.stack.isHomeStack()) {
+ // Never persist the home stack.
+ return;
+ }
+ mTaskPersister.wakeup(task, flush);
}
@Override
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 38eef208..0180db3 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -39,15 +39,17 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
-import java.util.LinkedList;
public class TaskPersister {
static final String TAG = "TaskPersister";
static final boolean DEBUG = false;
- /** When in slow mode don't write tasks out faster than this */
- private static final long INTER_TASK_DELAY_MS = 10000;
- private static final long DEBUG_INTER_TASK_DELAY_MS = 5000;
+ /** When not flushing don't write out files faster than this */
+ private static final long INTER_WRITE_DELAY_MS = 500;
+
+ /** When not flushing delay this long before writing the first file out. This gives the next
+ * task being launched a chance to load its resources without this occupying IO bandwidth. */
+ private static final long PRE_TASK_DELAY_MS = 3000;
private static final String RECENTS_FILENAME = "_task";
private static final String TASKS_DIRNAME = "recent_tasks";
@@ -63,10 +65,34 @@
private final ActivityManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
- private boolean mRecentsChanged = false;
+ /** Value determines write delay mode as follows:
+ * < 0 We are Flushing. No delays between writes until the image queue is drained and all
+ * tasks needing persisting are written to disk. There is no delay between writes.
+ * == 0 We are Idle. Next writes will be delayed by #PRE_TASK_DELAY_MS.
+ * > 0 We are Actively writing. Next write will be at this time. Subsequent writes will be
+ * delayed by #INTER_WRITE_DELAY_MS. */
+ private long mNextWriteTime = 0;
private final LazyTaskWriterThread mLazyTaskWriterThread;
+ private static class WriteQueueItem {}
+ private static class TaskWriteQueueItem extends WriteQueueItem {
+ final TaskRecord mTask;
+ TaskWriteQueueItem(TaskRecord task) {
+ mTask = task;
+ }
+ }
+ private static class ImageWriteQueueItem extends WriteQueueItem {
+ final String mFilename;
+ Bitmap mImage;
+ ImageWriteQueueItem(String filename, Bitmap image) {
+ mFilename = filename;
+ mImage = image;
+ }
+ }
+
+ ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
+
TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor) {
sTasksDir = new File(systemDir, TASKS_DIRNAME);
if (!sTasksDir.exists()) {
@@ -94,19 +120,77 @@
mLazyTaskWriterThread.start();
}
- public void notify(TaskRecord task, boolean flush) {
- if (DEBUG) Slog.d(TAG, "notify: task=" + task + " flush=" + flush +
- " Callers=" + Debug.getCallers(4));
- if (task != null) {
- task.needsPersisting = true;
- }
+ void wakeup(TaskRecord task, boolean flush) {
synchronized (this) {
- mLazyTaskWriterThread.mSlow = !flush;
- mRecentsChanged = true;
+ if (task != null) {
+ int queueNdx;
+ for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
+ final WriteQueueItem item = mWriteQueue.get(queueNdx);
+ if (item instanceof TaskWriteQueueItem &&
+ ((TaskWriteQueueItem) item).mTask == task) {
+ break;
+ }
+ }
+ if (queueNdx < 0) {
+ mWriteQueue.add(new TaskWriteQueueItem(task));
+ }
+ } else {
+ // Dummy.
+ mWriteQueue.add(new WriteQueueItem());
+ }
+ if (flush) {
+ mNextWriteTime = -1;
+ } else if (mNextWriteTime == 0) {
+ mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
+ }
+ if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
+ + mNextWriteTime + " Callers=" + Debug.getCallers(4));
notifyAll();
}
}
+ void saveImage(Bitmap image, String filename) {
+ synchronized (this) {
+ int queueNdx;
+ for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
+ final WriteQueueItem item = mWriteQueue.get(queueNdx);
+ if (item instanceof ImageWriteQueueItem) {
+ ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
+ if (imageWriteQueueItem.mFilename.equals(filename)) {
+ // replace the Bitmap with the new one.
+ imageWriteQueueItem.mImage = image;
+ break;
+ }
+ }
+ }
+ if (queueNdx < 0) {
+ mWriteQueue.add(new ImageWriteQueueItem(filename, image));
+ }
+ if (mNextWriteTime == 0) {
+ mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
+ }
+ if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
+ SystemClock.uptimeMillis() + " mNextWriteTime=" +
+ mNextWriteTime + " Callers=" + Debug.getCallers(4));
+ notifyAll();
+ }
+ }
+
+ Bitmap getThumbnail(String filename) {
+ synchronized (this) {
+ for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
+ final WriteQueueItem item = mWriteQueue.get(queueNdx);
+ if (item instanceof ImageWriteQueueItem) {
+ ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
+ if (imageWriteQueueItem.mFilename.equals(filename)) {
+ return imageWriteQueueItem.mImage;
+ }
+ }
+ }
+ return null;
+ }
+ }
+
private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
final XmlSerializer xmlSerializer = new FastXmlSerializer();
@@ -129,10 +213,6 @@
return stringWriter;
}
- void saveImage(Bitmap image, String filename) {
- mLazyTaskWriterThread.saveImage(image, filename);
- }
-
private String fileToString(File file) {
final String newline = System.lineSeparator();
try {
@@ -197,7 +277,7 @@
task);
if (task != null) {
task.isPersistable = true;
- task.needsPersisting = true;
+ mWriteQueue.add(new TaskWriteQueueItem(task));
tasks.add(task);
final int taskId = task.taskId;
recoveredTaskIds.add(taskId);
@@ -262,6 +342,8 @@
}
private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds=" + persistentTaskIds +
+ " files=" + files);
for (int fileNdx = 0; fileNdx < files.length; ++fileNdx) {
File file = files[fileNdx];
String filename = file.getName();
@@ -270,6 +352,7 @@
final int taskId;
try {
taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId);
} catch (Exception e) {
Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
file.delete();
@@ -295,63 +378,89 @@
}
private class LazyTaskWriterThread extends Thread {
- boolean mSlow = true;
- LinkedList<BitmapQueueEntry> mSaveImagesQueue = new LinkedList<BitmapQueueEntry>();
LazyTaskWriterThread(String name) {
super(name);
}
- class BitmapQueueEntry {
- final Bitmap mImage;
- final String mFilename;
- BitmapQueueEntry(Bitmap image, String filename) {
- mImage = image;
- mFilename = filename;
- }
- }
-
- void saveImage(Bitmap image, String filename) {
- synchronized (mSaveImagesQueue) {
- mSaveImagesQueue.add(new BitmapQueueEntry(image, filename));
- }
- TaskPersister.this.notify(null, false);
- }
-
@Override
public void run() {
ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>();
while (true) {
- // If mSlow, then delay between each call to saveToXml().
+ // We can't lock mService while holding TaskPersister.this, but we don't want to
+ // call removeObsoleteFiles every time through the loop, only the last time before
+ // going to sleep. The risk is that we call removeObsoleteFiles() successively.
+ final boolean probablyDone;
synchronized (TaskPersister.this) {
+ probablyDone = mWriteQueue.isEmpty();
+ }
+ if (probablyDone) {
+ if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
+ persistentTaskIds.clear();
+ synchronized (mService) {
+ final ArrayList<TaskRecord> tasks = mService.mRecentTasks;
+ if (DEBUG) Slog.d(TAG, "mRecents=" + tasks);
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = tasks.get(taskNdx);
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" +
+ task.isPersistable);
+ if (task.isPersistable && !task.stack.isHomeStack()) {
+ if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
+ persistentTaskIds.add(task.taskId);
+ } else {
+ if (DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + task);
+ }
+ }
+ }
+ removeObsoleteFiles(persistentTaskIds);
+ }
+
+ // If mNextWriteTime, then don't delay between each call to saveToXml().
+ final WriteQueueItem item;
+ synchronized (TaskPersister.this) {
+ if (mNextWriteTime >= 0) {
+ // The next write we don't have to wait so long.
+ mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
+ if (DEBUG) Slog.d(TAG, "Next write time may be in " +
+ INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
+ }
+
+ while (mWriteQueue.isEmpty()) {
+ mNextWriteTime = 0; // idle.
+ try {
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
+ TaskPersister.this.wait();
+ } catch (InterruptedException e) {
+ }
+ // Invariant: mNextWriteTime is either -1 or PRE_WRITE_DELAY_MS from now.
+ }
+ item = mWriteQueue.remove(0);
+
long now = SystemClock.uptimeMillis();
- final long releaseTime =
- now + (DEBUG ? DEBUG_INTER_TASK_DELAY_MS: INTER_TASK_DELAY_MS);
- while (mSlow && now < releaseTime) {
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
+ mNextWriteTime);
+ while (now < mNextWriteTime) {
try {
if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
- (releaseTime - now));
- TaskPersister.this.wait(releaseTime - now);
+ (mNextWriteTime - now));
+ TaskPersister.this.wait(mNextWriteTime - now);
} catch (InterruptedException e) {
}
now = SystemClock.uptimeMillis();
}
+
+ // Got something to do.
}
- // Write out one bitmap that needs saving each time through.
- BitmapQueueEntry entry;
- synchronized (mSaveImagesQueue) {
- entry = mSaveImagesQueue.poll();
- // Are there any more after this one?
- mRecentsChanged |= !mSaveImagesQueue.isEmpty();
- }
- if (entry != null) {
- final String filename = entry.mFilename;
- if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename);
+ if (item instanceof ImageWriteQueueItem) {
+ ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
+ final String filename = imageWriteQueueItem.mFilename;
+ final Bitmap bitmap = imageWriteQueueItem.mImage;
+ if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filename);
FileOutputStream imageFile = null;
try {
imageFile = new FileOutputStream(new File(sImagesDir, filename));
- entry.mImage.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
} catch (Exception e) {
Slog.e(TAG, "saveImage: unable to save " + filename, e);
} finally {
@@ -362,79 +471,42 @@
}
}
}
- }
-
- StringWriter stringWriter = null;
- TaskRecord task = null;
- synchronized(mService) {
- final ArrayList<TaskRecord> tasks = mService.mRecentTasks;
- persistentTaskIds.clear();
- if (DEBUG) Slog.d(TAG, "mRecents=" + tasks);
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- task = tasks.get(taskNdx);
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" +
- task.isPersistable + " needsPersisting=" + task.needsPersisting);
- if (task.isPersistable && !task.stack.isHomeStack()) {
- if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
- persistentTaskIds.add(task.taskId);
-
- if (task.needsPersisting) {
- try {
- if (DEBUG) Slog.d(TAG, "Saving task=" + task);
- stringWriter = saveToXml(task);
- break;
- } catch (IOException e) {
- } catch (XmlPullParserException e) {
- } finally {
- task.needsPersisting = false;
- }
- }
- } else {
- if (DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + task);
- }
- }
- }
-
- if (stringWriter != null) {
- // Write out xml file while not holding mService lock.
- FileOutputStream file = null;
- AtomicFile atomicFile = null;
- try {
- atomicFile = new AtomicFile(new File(sTasksDir,
- String.valueOf(task.taskId) + RECENTS_FILENAME + TASK_EXTENSION));
- file = atomicFile.startWrite();
- file.write(stringWriter.toString().getBytes());
- file.write('\n');
- atomicFile.finishWrite(file);
- } catch (IOException e) {
- if (file != null) {
- atomicFile.failWrite(file);
- }
- Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " + e);
- }
- } else {
- // Made it through the entire list and didn't find anything new that needed
- // persisting.
- if (!DEBUG) {
- if (DEBUG) Slog.d(TAG, "Calling removeObsoleteFiles persistentTaskIds=" +
- persistentTaskIds);
- removeObsoleteFiles(persistentTaskIds);
- }
-
- // Wait here for someone to call setRecentsChanged().
- synchronized (TaskPersister.this) {
- while (!mRecentsChanged) {
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: Waiting.");
+ } else if (item instanceof TaskWriteQueueItem) {
+ // Write out one task.
+ StringWriter stringWriter = null;
+ TaskRecord task = ((TaskWriteQueueItem) item).mTask;
+ if (DEBUG) Slog.d(TAG, "Writing task=" + task);
+ synchronized (mService) {
+ if (mService.mRecentTasks.contains(task)) {
+ // Still there.
try {
- TaskPersister.this.wait();
- } catch (InterruptedException e) {
+ if (DEBUG) Slog.d(TAG, "Saving task=" + task);
+ stringWriter = saveToXml(task);
+ } catch (IOException e) {
+ } catch (XmlPullParserException e) {
}
}
- mRecentsChanged = false;
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: Awake");
+ if (stringWriter != null) {
+ // Write out xml file while not holding mService lock.
+ FileOutputStream file = null;
+ AtomicFile atomicFile = null;
+ try {
+ atomicFile = new AtomicFile(new File(sTasksDir, String.valueOf(
+ task.taskId) + RECENTS_FILENAME + TASK_EXTENSION));
+ file = atomicFile.startWrite();
+ file.write(stringWriter.toString().getBytes());
+ file.write('\n');
+ atomicFile.finishWrite(file);
+ } catch (IOException e) {
+ if (file != null) {
+ atomicFile.failWrite(file);
+ }
+ Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " +
+ e);
+ }
+ }
}
}
- // Some recents file needs to be written.
}
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index d6f922f..4a84941 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -122,9 +122,6 @@
* (positive) or back (negative). Absolute value indicates time. */
long mLastTimeMoved = System.currentTimeMillis();
- /** True if persistable, has changed, and has not yet been persisted */
- boolean needsPersisting = false;
-
/** Indication of what to run next when task exits. Use ActivityRecord types.
* ActivityRecord.APPLICATION_ACTIVITY_TYPE indicates to resume the task below this one in the
* task stack. */
@@ -362,6 +359,9 @@
void getLastThumbnail(TaskThumbnail thumbs) {
thumbs.mainThumbnail = mLastThumbnail;
thumbs.thumbnailFileDescriptor = null;
+ if (mLastThumbnail == null) {
+ thumbs.mainThumbnail = mService.mTaskPersister.getThumbnail(mFilename);
+ }
if (mLastThumbnailFile.exists()) {
try {
thumbs.thumbnailFileDescriptor = ParcelFileDescriptor.open(mLastThumbnailFile,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 40eb3e44..c16be50 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -20,13 +20,13 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.SystemProperties;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -53,6 +53,9 @@
int logicalAddress;
int physicalAddress;
+ public ActiveSource() {
+ invalidate();
+ }
public ActiveSource(int logical, int physical) {
logicalAddress = logical;
physicalAddress = physical;
@@ -63,6 +66,10 @@
public boolean isValid() {
return HdmiUtils.isValidAddress(logicalAddress);
}
+ public void invalidate() {
+ logicalAddress = Constants.ADDR_INVALID;
+ physicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
+ }
public boolean equals(int logical, int physical) {
return logicalAddress == logical && physicalAddress == physical;
}
@@ -82,8 +89,7 @@
}
// Logical address of the active source.
@GuardedBy("mLock")
- protected final ActiveSource mActiveSource =
- new ActiveSource(-1, Constants.INVALID_PHYSICAL_ADDRESS);
+ protected final ActiveSource mActiveSource = new ActiveSource();
// Active routing path. Physical address of the active source but not all the time, such as
// when the new active source does not claim itself to be one. Note that we don't keep
@@ -505,9 +511,12 @@
@ServiceThreadOnly
<T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
assertRunOnServiceThread();
- ArrayList<T> actions = new ArrayList<>();
+ List<T> actions = Collections.<T>emptyList();
for (FeatureAction action : mActions) {
if (action.getClass().equals(clazz)) {
+ if (actions.isEmpty()) {
+ actions = new ArrayList<T>();
+ }
actions.add((T) action);
}
}
@@ -552,7 +561,10 @@
protected void checkIfPendingActionsCleared() {
if (mActions.isEmpty() && mPendingActionClearedCallback != null) {
- mPendingActionClearedCallback.onCleared(this);
+ PendingActionClearedCallback callback = mPendingActionClearedCallback;
+ // To prevent from calling the callback again during handling the callback itself.
+ mPendingActionClearedCallback = null;
+ callback.onCleared(this);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index f93d20f..9038fbc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -234,10 +234,10 @@
void updateActiveInput(int path, boolean notifyInputChange) {
assertRunOnServiceThread();
// Seq #15
- int portId = mService.pathToPortId(path);
- if (portId == getActivePortId()) {
+ if (path == getActivePath()) {
return;
}
+ int portId = mService.pathToPortId(path);
setActivePath(path);
setPrevPortId(portId);
// TODO: Handle PAP/PIP case.
@@ -265,7 +265,7 @@
invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
return;
}
- setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS);
+ mActiveSource.invalidate();
if (!mService.isControlEnabled()) {
setActivePortId(portId);
invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
@@ -480,9 +480,9 @@
byte[] params = message.getParams();
int currentPath = HdmiUtils.twoBytesToInt(params);
if (HdmiUtils.isAffectingActiveRoutingPath(getActivePath(), currentPath)) {
- int newPath = HdmiUtils.twoBytesToInt(params, 2);
- setActivePath(newPath);
+ mActiveSource.invalidate();
removeAction(RoutingControlAction.class);
+ int newPath = HdmiUtils.twoBytesToInt(params, 2);
addAndStartAction(new RoutingControlAction(this, newPath, true, null));
}
return true;
@@ -1249,18 +1249,6 @@
}
}
- @Override
- @ServiceThreadOnly
- protected boolean handleStandby(HdmiCecMessage message) {
- assertRunOnServiceThread();
- // Seq #12
- // Tv accepts directly addressed <Standby> only.
- if (message.getDestination() == mAddress) {
- super.handleStandby(message);
- }
- return false;
- }
-
boolean isProhibitMode() {
return mService.isProhibitMode();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 2272283..390d121 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -200,10 +200,10 @@
private List<HdmiPortInfo> mPortInfo;
// Map from path(physical address) to port ID.
- private SparseIntArray mPortIdMap = new SparseIntArray();
+ private final SparseIntArray mPortIdMap = new SparseIntArray();
// Map from port ID to HdmiPortInfo.
- private SparseArray<HdmiPortInfo> mPortInfoMap = new SparseArray<>();
+ private final SparseArray<HdmiPortInfo> mPortInfoMap = new SparseArray<>();
private HdmiCecMessageValidator mMessageValidator;
@@ -1414,8 +1414,8 @@
Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
devices.remove(device);
if (devices.isEmpty()) {
- clearLocalDevices();
onStandbyCompleted();
+ clearLocalDevices();
}
}
});
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 65e617b..077d16f 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -20,6 +20,7 @@
import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiHotplugEvent;
import android.hardware.hdmi.IHdmiControlService;
@@ -34,6 +35,7 @@
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
import android.media.tv.TvInputHardwareInfo;
+import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.media.tv.TvStreamConfig;
import android.os.Handler;
@@ -759,8 +761,21 @@
private final class HdmiInputChangeListener extends IHdmiInputChangeListener.Stub {
@Override
public void onChanged(HdmiCecDeviceInfo device) throws RemoteException {
- // TODO: Build a channel Uri for the TvInputInfo associated with the logical device
- // and send an intent to TV app
+ String inputId;
+ synchronized (mLock) {
+ if (device.isCecDevice()) {
+ inputId = mHdmiCecInputIdMap.get(device.getLogicalAddress());
+ } else {
+ inputId = findInputIdForHdmiPortLocked(device.getPortId());
+ }
+ }
+ if (inputId != null) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(TvContract.buildChannelUriForPassthroughTvInput(inputId));
+ mContext.startActivity(intent);
+ } else {
+ Slog.w(TAG, "onChanged: InputId cannot be found for :" + device);
+ }
}
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
index f1284d80..4b60c9f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
@@ -17,6 +17,7 @@
package com.android.server.devicepolicy;
import android.app.AppGlobals;
+import android.content.ComponentName;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -53,6 +54,7 @@
private static final String TAG_PROFILE_OWNER = "profile-owner";
private static final String ATTR_NAME = "name";
private static final String ATTR_PACKAGE = "package";
+ private static final String ATTR_COMPONENT_NAME = "component";
private static final String ATTR_USERID = "userId";
private AtomicFile fileForWriting;
@@ -100,6 +102,7 @@
}
/**
+ * @deprecated Use a component name instead of package name
* Creates an instance of the device owner object with the profile owner set.
*/
static DeviceOwner createWithProfileOwner(String packageName, String ownerName, int userId) {
@@ -108,6 +111,15 @@
return owner;
}
+ /**
+ * Creates an instance of the device owner object with the profile owner set.
+ */
+ static DeviceOwner createWithProfileOwner(ComponentName admin, String ownerName, int userId) {
+ DeviceOwner owner = new DeviceOwner();
+ owner.mProfileOwners.put(userId, new OwnerInfo(ownerName, admin));
+ return owner;
+ }
+
String getDeviceOwnerPackageName() {
return mDeviceOwner != null ? mDeviceOwner.packageName : null;
}
@@ -124,19 +136,36 @@
mDeviceOwner = null;
}
+ /**
+ * @deprecated
+ */
void setProfileOwner(String packageName, String ownerName, int userId) {
mProfileOwners.put(userId, new OwnerInfo(ownerName, packageName));
}
+ void setProfileOwner(ComponentName admin, String ownerName, int userId) {
+ mProfileOwners.put(userId, new OwnerInfo(ownerName, admin));
+ }
+
void removeProfileOwner(int userId) {
mProfileOwners.remove(userId);
}
+ /**
+ * @deprecated Use getProfileOwnerComponent
+ * @param userId
+ * @return
+ */
String getProfileOwnerPackageName(int userId) {
OwnerInfo profileOwner = mProfileOwners.get(userId);
return profileOwner != null ? profileOwner.packageName : null;
}
+ ComponentName getProfileOwnerComponent(int userId) {
+ OwnerInfo profileOwner = mProfileOwners.get(userId);
+ return profileOwner != null ? profileOwner.admin : null;
+ }
+
String getProfileOwnerName(int userId) {
OwnerInfo profileOwner = mProfileOwners.get(userId);
return profileOwner != null ? profileOwner.name : null;
@@ -191,15 +220,23 @@
String tag = parser.getName();
if (tag.equals(TAG_DEVICE_OWNER)) {
- mDeviceOwner = new OwnerInfo(
- parser.getAttributeValue(null, ATTR_NAME),
- parser.getAttributeValue(null, ATTR_PACKAGE));
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+ mDeviceOwner = new OwnerInfo(name, packageName);
} else if (tag.equals(TAG_PROFILE_OWNER)) {
String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME);
+ String profileOwnerComponentStr =
+ parser.getAttributeValue(null, ATTR_COMPONENT_NAME);
int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USERID));
- mProfileOwners.put(userId,
- new OwnerInfo(profileOwnerName, profileOwnerPackageName));
+ OwnerInfo profileOwnerInfo;
+ if (profileOwnerComponentStr != null) {
+ profileOwnerInfo = new OwnerInfo(profileOwnerName,
+ ComponentName.unflattenFromString(profileOwnerComponentStr));
+ } else {
+ profileOwnerInfo = new OwnerInfo(profileOwnerName, profileOwnerPackageName);
+ }
+ mProfileOwners.put(userId, profileOwnerInfo);
} else {
throw new XmlPullParserException(
"Unexpected tag in device owner file: " + tag);
@@ -230,9 +267,6 @@
if (mDeviceOwner != null) {
out.startTag(null, TAG_DEVICE_OWNER);
out.attribute(null, ATTR_PACKAGE, mDeviceOwner.packageName);
- if (mDeviceOwner.packageName != null) {
- out.attribute(null, ATTR_NAME, mDeviceOwner.packageName);
- }
out.endTag(null, TAG_DEVICE_OWNER);
}
@@ -240,9 +274,13 @@
if (mProfileOwners.size() > 0) {
for (HashMap.Entry<Integer, OwnerInfo> owner : mProfileOwners.entrySet()) {
out.startTag(null, TAG_PROFILE_OWNER);
- out.attribute(null, ATTR_PACKAGE, owner.getValue().packageName);
- out.attribute(null, ATTR_NAME, owner.getValue().name);
+ OwnerInfo ownerInfo = owner.getValue();
+ out.attribute(null, ATTR_PACKAGE, ownerInfo.packageName);
+ out.attribute(null, ATTR_NAME, ownerInfo.name);
out.attribute(null, ATTR_USERID, Integer.toString(owner.getKey()));
+ if (ownerInfo.admin != null) {
+ out.attribute(null, ATTR_COMPONENT_NAME, ownerInfo.admin.flattenToString());
+ }
out.endTag(null, TAG_PROFILE_OWNER);
}
}
@@ -282,10 +320,18 @@
static class OwnerInfo {
public String name;
public String packageName;
+ public ComponentName admin;
public OwnerInfo(String name, String packageName) {
this.name = name;
this.packageName = packageName;
+ this.admin = new ComponentName(packageName, "");
+ }
+
+ public OwnerInfo(String name, ComponentName admin) {
+ this.name = name;
+ this.admin = admin;
+ this.packageName = admin.getPackageName();
}
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f49451e..15bea5a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -905,7 +905,8 @@
for (ActiveAdmin admin : candidates) {
boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
boolean ownsProfile = (getProfileOwner(userHandle) != null
- && getProfileOwner(userHandle).equals(admin.info.getPackageName()));
+ && getProfileOwner(userHandle).getPackageName()
+ .equals(admin.info.getPackageName()));
if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
if (ownsDevice) {
@@ -3310,7 +3311,7 @@
}
@Override
- public boolean setProfileOwner(String packageName, String ownerName, int userHandle) {
+ public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) {
if (!mHasFeature) {
return false;
}
@@ -3322,26 +3323,28 @@
"Attempted to set profile owner for invalid userId: " + userHandle);
}
- if (packageName == null
- || !DeviceOwner.isInstalledForUser(packageName, userHandle)) {
- throw new IllegalArgumentException("Package name " + packageName
+ if (who == null
+ || !DeviceOwner.isInstalledForUser(who.getPackageName(), userHandle)) {
+ throw new IllegalArgumentException("Component " + who
+ " not installed for userId:" + userHandle);
}
synchronized (this) {
- if (isUserSetupComplete(userHandle)) {
+ // Only SYSTEM_UID can override the userSetupComplete
+ if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID
+ && isUserSetupComplete(userHandle)) {
throw new IllegalStateException(
"Trying to set profile owner but user is already set-up.");
}
if (mDeviceOwner == null) {
// Device owner state does not exist, create it.
- mDeviceOwner = DeviceOwner.createWithProfileOwner(packageName, ownerName,
+ mDeviceOwner = DeviceOwner.createWithProfileOwner(who, ownerName,
userHandle);
mDeviceOwner.writeOwnerFile();
return true;
} else {
// Device owner already exists, update it.
- mDeviceOwner.setProfileOwner(packageName, ownerName, userHandle);
+ mDeviceOwner.setProfileOwner(who, ownerName, userHandle);
mDeviceOwner.writeOwnerFile();
return true;
}
@@ -3349,6 +3352,38 @@
}
@Override
+ public void clearProfileOwner(ComponentName who) {
+ if (!mHasFeature) {
+ return;
+ }
+ UserHandle callingUser = Binder.getCallingUserHandle();
+ // Check if this is the profile owner who is calling
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mUserManager.setUserRestrictions(new Bundle(), callingUser);
+ if (mDeviceOwner != null) {
+ mDeviceOwner.removeProfileOwner(callingUser.getIdentifier());
+ mDeviceOwner.writeOwnerFile();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
+ public boolean hasUserSetupCompleted() {
+ if (!mHasFeature) {
+ return true;
+ }
+ DevicePolicyData policy = getUserData(UserHandle.getCallingUserId());
+ // If policy is null, return true, else check if the setup has completed.
+ return policy == null || policy.mUserSetupComplete;
+ }
+
+ @Override
public void setProfileEnabled(ComponentName who) {
if (!mHasFeature) {
return;
@@ -3397,14 +3432,14 @@
}
@Override
- public String getProfileOwner(int userHandle) {
+ public ComponentName getProfileOwner(int userHandle) {
if (!mHasFeature) {
return null;
}
synchronized (this) {
if (mDeviceOwner != null) {
- return mDeviceOwner.getProfileOwnerPackageName(userHandle);
+ return mDeviceOwner.getProfileOwnerComponent(userHandle);
}
}
return null;
@@ -3413,8 +3448,9 @@
// Returns the active profile owner for this user or null if the current user has no
// profile owner.
private ActiveAdmin getProfileOwnerAdmin(int userHandle) {
- String profileOwnerPackage = getProfileOwner(userHandle);
- if (profileOwnerPackage == null) {
+ ComponentName profileOwner =
+ mDeviceOwner != null ? mDeviceOwner.getProfileOwnerComponent(userHandle) : null;
+ if (profileOwner == null) {
return null;
}
@@ -3422,7 +3458,7 @@
final int n = policy.mAdminList.size();
for (int i = 0; i < n; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
- if (profileOwnerPackage.equals(admin.info.getPackageName())) {
+ if (profileOwner.equals(admin.info)) {
return admin;
}
}
@@ -3800,7 +3836,7 @@
}
setActiveAdmin(profileOwnerComponent, true, user.getIdentifier(), adminExtras);
- setProfileOwner(profileOwnerPkg, ownerName, user.getIdentifier());
+ setProfileOwner(profileOwnerComponent, ownerName, user.getIdentifier());
return user;
} finally {
restoreCallingIdentity(id);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 70266ee..d955354 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -406,6 +406,7 @@
TelephonyRegistry telephonyRegistry = null;
ConsumerIrService consumerIr = null;
AudioService audioService = null;
+ MmsServiceBroker mmsService = null;
boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
@@ -976,6 +977,9 @@
VMRuntime.getRuntime().startJitCompilation();
}
+ // MMS service broker
+ mmsService = mSystemServiceManager.startService(MmsServiceBroker.class);
+
// It is now time to start up the app processes...
try {
@@ -1057,6 +1061,7 @@
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
final MediaRouterService mediaRouterF = mediaRouter;
final AudioService audioServiceF = audioService;
+ final MmsServiceBroker mmsServiceF = mmsService;
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
@@ -1187,6 +1192,11 @@
reportWtf("Notifying MediaRouterService running", e);
}
+ try {
+ if (mmsServiceF != null) mmsServiceF.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying MmsService running", e);
+ }
mSystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETE);
}
});
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 94f227c..c0e536a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -96,13 +96,6 @@
@Override
public void onServiceDisconnected(ComponentName name) {
- try {
- if (mService != null) {
- mService.shutdown();
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in shutdown", e);
- }
mService = null;
}
};
@@ -307,6 +300,14 @@
}
void shutdownLocked() {
+ try {
+ if (mService != null) {
+ mService.shutdown();
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in shutdown", e);
+ }
+
if (mBound) {
mContext.unbindService(mConnection);
mBound = false;
diff --git a/telecomm/java/android/telecomm/CallCameraCapabilities.java b/telecomm/java/android/telecomm/CallCameraCapabilities.java
index 87a411a..74904e3 100644
--- a/telecomm/java/android/telecomm/CallCameraCapabilities.java
+++ b/telecomm/java/android/telecomm/CallCameraCapabilities.java
@@ -36,14 +36,28 @@
private final float mMaxZoom;
/**
+ * The width of the camera video in pixels.
+ */
+ private final int mWidth;
+
+ /**
+ * The height of the camera video in pixels.
+ */
+ private final int mHeight;
+
+ /**
* Create a call camera capabilities instance.
*
* @param zoomSupported True when camera supports zoom.
* @param maxZoom Maximum zoom supported by camera.
+ * @param width The width of the camera video (in pixels).
+ * @param height The height of the camera video (in pixels).
*/
- public CallCameraCapabilities(boolean zoomSupported, float maxZoom) {
+ public CallCameraCapabilities(boolean zoomSupported, float maxZoom, int width, int height) {
mZoomSupported = zoomSupported;
mMaxZoom = maxZoom;
+ mWidth = width;
+ mHeight = height;
}
/**
@@ -61,8 +75,10 @@
public CallCameraCapabilities createFromParcel(Parcel source) {
boolean supportsZoom = source.readByte() != 0;
float maxZoom = source.readFloat();
+ int width = source.readInt();
+ int height = source.readInt();
- return new CallCameraCapabilities(supportsZoom, maxZoom);
+ return new CallCameraCapabilities(supportsZoom, maxZoom, width, height);
}
@Override
@@ -94,7 +110,8 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (isZoomSupported() ? 1 : 0));
dest.writeFloat(getMaxZoom());
-
+ dest.writeInt(getWidth());
+ dest.writeInt(getHeight());
}
/**
@@ -110,4 +127,18 @@
public float getMaxZoom() {
return mMaxZoom;
}
+
+ /**
+ * The width of the camera video in pixels.
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * The height of the camera video in pixels.
+ */
+ public int getHeight() {
+ return mHeight;
+ }
}
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index fb719a3..1484021 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -121,9 +121,15 @@
}
@Override
- public void createConnection(ConnectionRequest request, boolean isIncoming) {
- mHandler.obtainMessage(
- MSG_CREATE_CONNECTION, isIncoming ? 1 : 0, 0, request).sendToTarget();
+ public void createConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request,
+ boolean isIncoming) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionManagerPhoneAccount;
+ args.arg2 = request;
+ args.argi1 = isIncoming ? 1 : 0;
+ mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
}
@Override
@@ -217,9 +223,19 @@
mAdapter.addAdapter((IConnectionServiceAdapter) msg.obj);
onAdapterAttached();
break;
- case MSG_CREATE_CONNECTION:
- createConnection((ConnectionRequest) msg.obj, msg.arg1 == 1);
+ case MSG_CREATE_CONNECTION: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ PhoneAccountHandle connectionManagerPhoneAccount =
+ (PhoneAccountHandle) args.arg1;
+ ConnectionRequest request = (ConnectionRequest) args.arg2;
+ boolean isIncoming = args.argi1 == 1;
+ createConnection(connectionManagerPhoneAccount, request, isIncoming);
+ } finally {
+ args.recycle();
+ }
break;
+ }
case MSG_ABORT:
abort((String) msg.obj);
break;
@@ -428,14 +444,17 @@
* incoming call. In either case, telecomm will cycle through a set of services and call
* createConnection util a connection service cancels the process or completes it successfully.
*/
- private void createConnection(final ConnectionRequest request, boolean isIncoming) {
+ private void createConnection(
+ final PhoneAccountHandle callManagerAccount,
+ final ConnectionRequest request,
+ boolean isIncoming) {
Log.d(this, "call %s", request);
final Connection createdConnection;
if (isIncoming) {
- createdConnection = onCreateIncomingConnection(request);
+ createdConnection = onCreateIncomingConnection(callManagerAccount, request);
} else {
- createdConnection = onCreateOutgoingConnection(request);
+ createdConnection = onCreateOutgoingConnection(callManagerAccount, request);
}
if (createdConnection != null) {
@@ -641,40 +660,94 @@
});
}
- public final RemoteConnection createRemoteIncomingConnection(ConnectionRequest request) {
- return mRemoteConnectionManager.createRemoteConnection(request, true);
- }
-
- public final RemoteConnection createRemoteOutgoingConnection(ConnectionRequest request) {
- return mRemoteConnectionManager.createRemoteConnection(request, false);
+ /**
+ * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
+ * incoming request. This is used to attach to existing incoming calls.
+ *
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request Details about the incoming call.
+ * @return The {@code Connection} object to satisfy this call, or {@code null} to
+ * not handle the call.
+ */
+ public final RemoteConnection createRemoteIncomingConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ return mRemoteConnectionManager.createRemoteConnection(
+ connectionManagerPhoneAccount, request, true);
}
/**
- * Returns all connections currently associated with this connection service.
+ * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
+ * outgoing request. This is used to initiate new outgoing calls.
+ *
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request Details about the incoming call.
+ * @return The {@code Connection} object to satisfy this call, or {@code null} to
+ * not handle the call.
+ */
+ public final RemoteConnection createRemoteOutgoingConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ return mRemoteConnectionManager.createRemoteConnection(
+ connectionManagerPhoneAccount, request, false);
+ }
+
+ /**
+ * Returns all the active {@code Connection}s for which this {@code ConnectionService}
+ * has taken responsibility.
+ *
+ * @return A collection of {@code Connection}s created by this {@code ConnectionService}.
*/
public final Collection<Connection> getAllConnections() {
return mConnectionById.values();
}
/**
- * Create a Connection given an incoming request. This is used to attach to existing incoming
- * calls.
- * @param request Details about the incoming call.
+ * Create a {@code Connection} given an incoming request. This is used to attach to existing
+ * incoming calls.
*
- * @return The {@link Connection} object to satisfy this call, or {@code null} to not handle
- * the call.
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request Details about the incoming call.
+ * @return The {@code Connection} object to satisfy this call, or {@code null} to
+ * not handle the call.
*/
- public Connection onCreateIncomingConnection(ConnectionRequest request) { return null; }
+ public Connection onCreateIncomingConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ return null;
+ }
/**
- * Create a Connection given an outgoing request. This is used to initiate new outgoing calls.
- * @param request Details about the outgoing call.
+ * Create a {@code Connection} given an outgoing request. This is used to initiate new
+ * outgoing calls.
*
- * @return The {@link Connection} object to satisfy this request,
- * or null to not handle the call.
- *
+ * @param connectionManagerPhoneAccount The connection manager account to use for managing
+ * this call.
+ * <p>
+ * If this parameter is not {@code null}, it means that this {@code ConnectionService}
+ * has registered one or more {@code PhoneAccount}s having
+ * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
+ * one of these {@code PhoneAccount}s, while the {@code request} will contain another
+ * (usually but not always distinct) {@code PhoneAccount} to be used for actually
+ * making the connection.
+ * <p>
+ * If this parameter is {@code null}, it means that this {@code ConnectionService} is
+ * being asked to make a direct connection. The
+ * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
+ * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
+ * making the connection.
+ * @param request Details about the outgoing call.
+ * @return The {@code Connection} object to satisfy this call, or the result of an invocation
+ * of {@link Connection#getFailedConnection(int, String)} to not handle the call.
*/
- public Connection onCreateOutgoingConnection(ConnectionRequest request) { return null; }
+ public Connection onCreateOutgoingConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ return null;
+ }
/**
* Returns a new or existing conference connection when the the user elects to convert the
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index 1f9071e..0fea7ba 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -32,24 +32,24 @@
public class PhoneAccount implements Parcelable {
/**
- * Flag indicating that this {@code PhoneAccount} can act as a call manager for
- * traditional SIM-based telephony calls. The {@link ConnectionService} associated with this
- * phone-account will be allowed to manage SIM-based phone calls including using its own
- * proprietary phone-call implementation (like VoIP calling) to make calls instead of the
- * telephony stack.
+ * Flag indicating that this {@code PhoneAccount} can act as a connection manager for
+ * other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
+ * will be allowed to manage phone calls including using its own proprietary phone-call
+ * implementation (like VoIP calling) to make calls instead of the telephony stack.
+ * <p>
* When a user opts to place a call using the SIM-based telephony stack, the connection-service
* associated with this phone-account will be attempted first if the user has explicitly
- * selected it to be used as the default call-manager.
+ * selected it to be used as the default connection manager.
* <p>
* See {@link #getCapabilities}
*/
- public static final int CAPABILITY_SIM_CALL_MANAGER = 0x1;
+ public static final int CAPABILITY_CONNECTION_MANAGER = 0x1;
/**
* Flag indicating that this {@code PhoneAccount} can make phone calls in place of
* traditional SIM-based telephony calls. This account will be treated as a distinct method
* for placing calls alongside the traditional SIM-based telephony stack. This flag is
- * distinct from {@link #CAPABILITY_SIM_CALL_MANAGER} in that it is not allowed to manage
+ * distinct from {@link #CAPABILITY_CONNECTION_MANAGER} in that it is not allowed to manage
* calls from or use the built-in telephony stack to place its calls.
* <p>
* See {@link #getCapabilities}
@@ -66,6 +66,11 @@
*/
public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4;
+ /**
+ * Flag indicating that this {@code PhoneAccount} is capable of video calling.
+ */
+ public static final int CAPABILITY_VIDEO_CALLING = 0x8;
+
private final PhoneAccountHandle mAccountHandle;
private final Uri mHandle;
private final String mSubscriptionNumber;
@@ -73,7 +78,6 @@
private final int mIconResId;
private final CharSequence mLabel;
private final CharSequence mShortDescription;
- private boolean mVideoCallingSupported;
public PhoneAccount(
PhoneAccountHandle account,
@@ -82,8 +86,7 @@
int capabilities,
int iconResId,
CharSequence label,
- CharSequence shortDescription,
- boolean supportsVideoCalling) {
+ CharSequence shortDescription) {
mAccountHandle = account;
mHandle = handle;
mSubscriptionNumber = subscriptionNumber;
@@ -91,7 +94,6 @@
mIconResId = iconResId;
mLabel = label;
mShortDescription = shortDescription;
- mVideoCallingSupported = supportsVideoCalling;
}
/**
@@ -189,15 +191,6 @@
}
}
- /**
- * Determines whether this {@code PhoneAccount} supports video calling.
- *
- * @return {@code true} if this {@code PhoneAccount} supports video calling.
- */
- public boolean isVideoCallingSupported() {
- return mVideoCallingSupported;
- }
-
//
// Parcelable implementation
//
@@ -216,7 +209,6 @@
out.writeInt(mIconResId);
out.writeCharSequence(mLabel);
out.writeCharSequence(mShortDescription);
- out.writeInt(mVideoCallingSupported ? 1 : 0);
}
public static final Creator<PhoneAccount> CREATOR
@@ -240,6 +232,5 @@
mIconResId = in.readInt();
mLabel = in.readCharSequence();
mShortDescription = in.readCharSequence();
- mVideoCallingSupported = in.readInt() == 1;
}
}
diff --git a/telecomm/java/android/telecomm/RemoteConnectionManager.java b/telecomm/java/android/telecomm/RemoteConnectionManager.java
index 3ca68a2..ce8bfbd 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionManager.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionManager.java
@@ -50,6 +50,7 @@
}
public RemoteConnection createRemoteConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
ConnectionRequest request,
boolean isIncoming) {
PhoneAccountHandle accountHandle = request.getAccountHandle();
@@ -65,7 +66,8 @@
RemoteConnectionService remoteService = mRemoteConnectionServices.get(componentName);
if (remoteService != null) {
- return remoteService.createRemoteConnection(request, isIncoming);
+ return remoteService.createRemoteConnection(
+ connectionManagerPhoneAccount, request, isIncoming);
}
return null;
}
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index e6f47d2a..501e843 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -32,8 +32,6 @@
import com.android.internal.telecomm.IVideoCallProvider;
import com.android.internal.telecomm.RemoteServiceCallback;
-import java.util.LinkedList;
-import java.util.List;
import java.util.UUID;
/**
@@ -421,7 +419,10 @@
release();
}
- final RemoteConnection createRemoteConnection(ConnectionRequest request, boolean isIncoming) {
+ final RemoteConnection createRemoteConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request,
+ boolean isIncoming) {
if (mConnectionId == null) {
String id = UUID.randomUUID().toString();
ConnectionRequest newRequest = new ConnectionRequest(
@@ -433,7 +434,10 @@
request.getVideoState());
mConnection = new RemoteConnection(mConnectionService, request, isIncoming);
try {
- mConnectionService.createConnection(newRequest, isIncoming);
+ mConnectionService.createConnection(
+ connectionManagerPhoneAccount,
+ newRequest,
+ isIncoming);
mConnectionId = id;
} catch (RemoteException e) {
mConnection = RemoteConnection.failure(DisconnectCause.ERROR_UNSPECIFIED,
diff --git a/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
index 05375c9..4b05fb9 100644
--- a/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
@@ -19,6 +19,7 @@
import android.os.Bundle;
import android.telecomm.CallAudioState;
import android.telecomm.ConnectionRequest;
+import android.telecomm.PhoneAccountHandle;
import com.android.internal.telecomm.IConnectionServiceAdapter;
@@ -32,7 +33,10 @@
oneway interface IConnectionService {
void addConnectionServiceAdapter(in IConnectionServiceAdapter adapter);
- void createConnection(in ConnectionRequest request, boolean isIncoming);
+ void createConnection(
+ in PhoneAccountHandle connectionManagerPhoneAccount,
+ in ConnectionRequest request,
+ boolean isIncoming);
void abort(String callId);