Merge "Unhide broadcasts for WiFi network changes." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 93360c1..13a28d4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -590,7 +590,6 @@
field public static final int fillBefore = 16843196; // 0x10101bc
field public static final int fillColor = 16843807; // 0x101041f
field public static final int fillEnabled = 16843343; // 0x101024f
- field public static final int fillOpacity = 16843806; // 0x101041e
field public static final int fillViewport = 16843130; // 0x101017a
field public static final int filter = 16843035; // 0x101011b
field public static final int filterTouchesWhenObscured = 16843460; // 0x10102c4
@@ -1190,7 +1189,6 @@
field public static final int strokeLineCap = 16843815; // 0x1010427
field public static final int strokeLineJoin = 16843816; // 0x1010428
field public static final int strokeMiterLimit = 16843817; // 0x1010429
- field public static final int strokeOpacity = 16843810; // 0x1010422
field public static final int strokeWidth = 16843811; // 0x1010423
field public static final int submitBackground = 16843914; // 0x101048a
field public static final int subtitle = 16843473; // 0x10102d1
@@ -8677,6 +8675,7 @@
method public abstract void onSuccess();
method public abstract void onUserActionRequired(android.content.Intent);
field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
+ field public static final int FAILURE_ABORTED = 5; // 0x5
field public static final int FAILURE_CONFLICT = 2; // 0x2
field public static final int FAILURE_INCOMPATIBLE = 4; // 0x4
field public static final int FAILURE_INVALID = 1; // 0x1
@@ -8706,9 +8705,12 @@
public static abstract class PackageInstaller.UninstallCallback {
ctor public PackageInstaller.UninstallCallback();
- method public abstract void onFailure(java.lang.String);
+ method public abstract void onFailure(int, java.lang.String, android.os.Bundle);
method public abstract void onSuccess();
method public abstract void onUserActionRequired(android.content.Intent);
+ field public static final int FAILURE_ABORTED = 2; // 0x2
+ field public static final int FAILURE_BLOCKED = 1; // 0x1
+ field public static final int FAILURE_UNKNOWN = 0; // 0x0
}
public class PackageItemInfo {
@@ -13236,10 +13238,10 @@
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+ field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
- field public static final int VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE = 16; // 0x10
field public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 4; // 0x4
}
@@ -16401,7 +16403,7 @@
public final class MediaProjection {
method public void addCallback(android.media.projection.MediaProjection.Callback, android.os.Handler);
method public android.media.AudioRecord createAudioRecord(int, int, int, int);
- method public android.hardware.display.VirtualDisplay createVirtualDisplay(java.lang.String, int, int, int, boolean, android.view.Surface, android.hardware.display.VirtualDisplay.Callbacks, android.os.Handler);
+ method public android.hardware.display.VirtualDisplay createVirtualDisplay(java.lang.String, int, int, int, int, android.view.Surface, android.hardware.display.VirtualDisplay.Callbacks, android.os.Handler);
method public void removeCallback(android.media.projection.MediaProjection.Callback);
method public void stop();
}
@@ -28600,6 +28602,7 @@
method public void swapWithBackgroundCall();
method public void unhold();
field public static final int STATE_ACTIVE = 4; // 0x4
+ field public static final int STATE_CONNECTING = 9; // 0x9
field public static final int STATE_DIALING = 1; // 0x1
field public static final int STATE_DISCONNECTED = 7; // 0x7
field public static final int STATE_HOLDING = 3; // 0x3
@@ -28692,6 +28695,7 @@
method public static android.telecomm.CallState valueOf(java.lang.String);
method public static final android.telecomm.CallState[] values();
enum_constant public static final android.telecomm.CallState ACTIVE;
+ enum_constant public static final android.telecomm.CallState CONNECTING;
enum_constant public static final android.telecomm.CallState DIALING;
enum_constant public static final android.telecomm.CallState DISCONNECTED;
enum_constant public static final android.telecomm.CallState NEW;
@@ -37498,6 +37502,7 @@
method public void clearChoices();
method public void clearTextFilter();
method public void deferNotifyDataSetChanged();
+ method public void fling(int);
method public int getCacheColorHint();
method public int getCheckedItemCount();
method public long[] getCheckedItemIds();
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index df0365e..e9297b9 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -161,11 +161,14 @@
return result;
}
+ // system APIs start here
+
/**
* Begin the process of restoring data from backup. See the
* {@link android.app.backup.RestoreSession} class for documentation on that process.
* @hide
*/
+ @SystemApi
public RestoreSession beginRestoreSession() {
RestoreSession session = null;
checkServiceBinder();
@@ -183,8 +186,6 @@
return session;
}
- // system APIs start here
-
/**
* Enable/disable the backup service entirely. When disabled, no backup
* or restore operations will take place. Data-changed notifications will
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index dc3bbc0..6adc2e0b 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -405,6 +405,25 @@
return BackupTransport.TRANSPORT_ERROR;
}
+ /**
+ * Tells the transport to cancel the currently-ongoing full backup operation. This
+ * will happen between {@link #performFullBackup()} and {@link #finishBackup()}
+ * if the OS needs to abort the backup operation for any reason, such as a crash in
+ * the application undergoing backup.
+ *
+ * <p>When it receives this call, the transport should discard any partial archive
+ * that it has stored so far. If possible it should also roll back to the previous
+ * known-good archive in its datastore.
+ *
+ * <p>If the transport receives this callback, it will <em>not</em> receive a
+ * call to {@link #finishBackup()}. It needs to tear down any ongoing backup state
+ * here.
+ */
+ public void cancelFullBackup() {
+ throw new UnsupportedOperationException(
+ "Transport cancelFullBackup() not implemented");
+ }
+
// ------------------------------------------------------------------------------------
// Full restore interfaces
diff --git a/core/java/android/app/backup/RestoreObserver.java b/core/java/android/app/backup/RestoreObserver.java
index dbddb78..b4a23d0 100644
--- a/core/java/android/app/backup/RestoreObserver.java
+++ b/core/java/android/app/backup/RestoreObserver.java
@@ -17,6 +17,8 @@
package android.app.backup;
import java.lang.String;
+
+import android.annotation.SystemApi;
import android.app.backup.RestoreSet;
/**
@@ -36,6 +38,7 @@
*
* @hide
*/
+ @SystemApi
public void restoreSetsAvailable(RestoreSet[] result) {
}
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index cc0d569..5223476 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -36,4 +36,6 @@
void uninstall(String packageName, int flags, in IPackageDeleteObserver2 observer, int userId);
void uninstallSplit(String packageName, String splitName, int flags, in IPackageDeleteObserver2 observer, int userId);
+
+ void setPermissionsResult(int sessionId, boolean accepted);
}
diff --git a/core/java/android/content/pm/InstallSessionInfo.java b/core/java/android/content/pm/InstallSessionInfo.java
index f263885..161bcde 100644
--- a/core/java/android/content/pm/InstallSessionInfo.java
+++ b/core/java/android/content/pm/InstallSessionInfo.java
@@ -16,7 +16,6 @@
package android.content.pm;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -33,8 +32,12 @@
/** {@hide} */
public String installerPackageName;
/** {@hide} */
+ public String resolvedBaseCodePath;
+ /** {@hide} */
public float progress;
/** {@hide} */
+ public boolean sealed;
+ /** {@hide} */
public boolean open;
/** {@hide} */
@@ -56,7 +59,9 @@
public InstallSessionInfo(Parcel source) {
sessionId = source.readInt();
installerPackageName = source.readString();
+ resolvedBaseCodePath = source.readString();
progress = source.readFloat();
+ sealed = source.readInt() != 0;
open = source.readInt() != 0;
mode = source.readInt();
@@ -149,7 +154,9 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(sessionId);
dest.writeString(installerPackageName);
+ dest.writeString(resolvedBaseCodePath);
dest.writeFloat(progress);
+ dest.writeInt(sealed ? 1 : 0);
dest.writeInt(open ? 1 : 0);
dest.writeInt(mode);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 01c080d..d70e22c 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -81,6 +81,10 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
+ /** {@hide} */
+ public static final String
+ ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS";
+
/**
* An integer session ID.
*
@@ -88,6 +92,9 @@
*/
public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
+ /** {@hide} */
+ public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK";
+
private final PackageManager mPm;
private final IPackageInstaller mInstaller;
private final int mUserId;
@@ -206,6 +213,15 @@
}
}
+ /** {@hide} */
+ public void setPermissionsResult(int sessionId, boolean accepted) {
+ try {
+ mInstaller.setPermissionsResult(sessionId, accepted);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
/**
* Events for observing session lifecycle.
* <p>
@@ -541,6 +557,26 @@
*/
public static abstract class UninstallCallback {
/**
+ * Generic unknown failure. The system will always try to provide a more
+ * specific failure reason, but in some rare cases this may be
+ * delivered.
+ */
+ public static final int FAILURE_UNKNOWN = 0;
+
+ /**
+ * This uninstall was blocked. The package may be required for core
+ * system operation, or the user may be restricted. Attempting to
+ * uninstall again will have the same result.
+ */
+ public static final int FAILURE_BLOCKED = 1;
+
+ /**
+ * This uninstall was actively aborted. For example, the user declined
+ * to uninstall. You may try to uninstall again.
+ */
+ public static final int FAILURE_ABORTED = 2;
+
+ /**
* User action is required to proceed. You can start the given intent
* activity to involve the user and continue.
* <p>
@@ -551,7 +587,7 @@
public abstract void onUserActionRequired(Intent intent);
public abstract void onSuccess();
- public abstract void onFailure(String msg);
+ public abstract void onFailure(int failureReason, String msg, Bundle extras);
}
/** {@hide} */
@@ -572,8 +608,9 @@
if (returnCode == PackageManager.DELETE_SUCCEEDED) {
target.onSuccess();
} else {
+ final int failureReason = PackageManager.deleteStatusToFailureReason(returnCode);
msg = PackageManager.deleteStatusToString(returnCode) + ": " + msg;
- target.onFailure(msg);
+ target.onFailure(failureReason, msg, null);
}
}
}
@@ -603,9 +640,8 @@
* permission, incompatible certificates, etc. The user may be able to
* uninstall another app to fix the issue.
* <p>
- * The extras bundle may contain {@link #EXTRA_PACKAGE_NAME} if one
- * specific package was identified as the cause of the conflict. If
- * unknown, or multiple packages, the extra may be {@code null}.
+ * The extras bundle may contain {@link #EXTRA_PACKAGE_NAME} with the
+ * specific packages identified as the cause of the conflict.
*/
public static final int FAILURE_CONFLICT = 2;
@@ -626,6 +662,15 @@
*/
public static final int FAILURE_INCOMPATIBLE = 4;
+ /**
+ * This install session failed because it was actively aborted. For
+ * example, the user declined requested permissions, or a verifier
+ * rejected the session.
+ *
+ * @see PackageManager#VERIFICATION_REJECT
+ */
+ public static final int FAILURE_ABORTED = 5;
+
public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1e4ed31..7dad367 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -28,6 +28,7 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.PackageInstaller.CommitCallback;
+import android.content.pm.PackageInstaller.UninstallCallback;
import android.content.pm.PackageParser.PackageParserException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -770,6 +771,9 @@
*/
public static final int NO_NATIVE_LIBRARIES = -114;
+ /** {@hide} */
+ public static final int INSTALL_FAILED_ABORTED = -115;
+
/**
* Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the
* package's data directory.
@@ -842,7 +846,10 @@
*
* @hide
*/
- public static final int DELETE_FAILED_OWNER_BLOCKED= -4;
+ public static final int DELETE_FAILED_OWNER_BLOCKED = -4;
+
+ /** {@hide} */
+ public static final int DELETE_FAILED_ABORTED = -5;
/**
* Return code that is passed to the {@link IPackageMoveObserver} by
@@ -3830,6 +3837,7 @@
case INSTALL_FAILED_USER_RESTRICTED: return "INSTALL_FAILED_USER_RESTRICTED";
case INSTALL_FAILED_DUPLICATE_PERMISSION: return "INSTALL_FAILED_DUPLICATE_PERMISSION";
case INSTALL_FAILED_NO_MATCHING_ABIS: return "INSTALL_FAILED_NO_MATCHING_ABIS";
+ case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED";
default: return Integer.toString(status);
}
}
@@ -3857,8 +3865,8 @@
case INSTALL_FAILED_CONTAINER_ERROR: return CommitCallback.FAILURE_STORAGE;
case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return CommitCallback.FAILURE_STORAGE;
case INSTALL_FAILED_MEDIA_UNAVAILABLE: return CommitCallback.FAILURE_STORAGE;
- case INSTALL_FAILED_VERIFICATION_TIMEOUT: return CommitCallback.FAILURE_UNKNOWN;
- case INSTALL_FAILED_VERIFICATION_FAILURE: return CommitCallback.FAILURE_UNKNOWN;
+ case INSTALL_FAILED_VERIFICATION_TIMEOUT: return CommitCallback.FAILURE_ABORTED;
+ case INSTALL_FAILED_VERIFICATION_FAILURE: return CommitCallback.FAILURE_ABORTED;
case INSTALL_FAILED_PACKAGE_CHANGED: return CommitCallback.FAILURE_INVALID;
case INSTALL_FAILED_UID_CHANGED: return CommitCallback.FAILURE_INVALID;
case INSTALL_FAILED_VERSION_DOWNGRADE: return CommitCallback.FAILURE_INVALID;
@@ -3876,6 +3884,7 @@
case INSTALL_FAILED_USER_RESTRICTED: return CommitCallback.FAILURE_INCOMPATIBLE;
case INSTALL_FAILED_DUPLICATE_PERMISSION: return CommitCallback.FAILURE_CONFLICT;
case INSTALL_FAILED_NO_MATCHING_ABIS: return CommitCallback.FAILURE_INCOMPATIBLE;
+ case INSTALL_FAILED_ABORTED: return CommitCallback.FAILURE_ABORTED;
default: return CommitCallback.FAILURE_UNKNOWN;
}
}
@@ -3888,7 +3897,20 @@
case DELETE_FAILED_DEVICE_POLICY_MANAGER: return "DELETE_FAILED_DEVICE_POLICY_MANAGER";
case DELETE_FAILED_USER_RESTRICTED: return "DELETE_FAILED_USER_RESTRICTED";
case DELETE_FAILED_OWNER_BLOCKED: return "DELETE_FAILED_OWNER_BLOCKED";
+ case DELETE_FAILED_ABORTED: return "DELETE_FAILED_ABORTED";
default: return Integer.toString(status);
}
}
+
+ /** {@hide} */
+ public static int deleteStatusToFailureReason(int status) {
+ switch (status) {
+ case DELETE_FAILED_INTERNAL_ERROR: return UninstallCallback.FAILURE_UNKNOWN;
+ case DELETE_FAILED_DEVICE_POLICY_MANAGER: return UninstallCallback.FAILURE_BLOCKED;
+ case DELETE_FAILED_USER_RESTRICTED: return UninstallCallback.FAILURE_BLOCKED;
+ case DELETE_FAILED_OWNER_BLOCKED: return UninstallCallback.FAILURE_BLOCKED;
+ case DELETE_FAILED_ABORTED: return UninstallCallback.FAILURE_ABORTED;
+ default: return UninstallCallback.FAILURE_UNKNOWN;
+ }
+ }
}
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 900b41d..3c290f7 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -233,7 +233,7 @@
}
if (alphaRes != 0) {
- alpha = r.getFraction(alphaRes, 1, 1);
+ alpha = r.getFloat(alphaRes);
}
// Apply alpha modulation.
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 5face69..52d1c79 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -986,6 +986,34 @@
}
/**
+ * Retrieve a floating-point value for a particular resource ID.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @return Returns the floating-point value contained in the resource.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does
+ * not exist or is not a floating-point value.
+ * @hide Pending API council approval.
+ */
+ public float getFloat(int id) {
+ synchronized (mAccessLock) {
+ TypedValue value = mTmpValue;
+ if (value == null) {
+ mTmpValue = value = new TypedValue();
+ }
+ getValue(id, value, true);
+ if (value.type == TypedValue.TYPE_FLOAT) {
+ return value.getFloat();
+ }
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ + Integer.toHexString(value.type) + " is not valid");
+ }
+ }
+
+ /**
* Return an XmlResourceParser through which you can read a view layout
* description for the given resource ID. This parser has limited
* functionality -- in particular, you can't change its input, and only
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index c461511..6a9d565 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -130,15 +130,6 @@
public abstract String getId();
/**
- * <p>Set up a new output set of Surfaces for the camera device.</p>
- *
- * @deprecated Use {@link #createCaptureSession} instead
- * @hide
- */
- @Deprecated
- public abstract void configureOutputs(List<Surface> outputs) throws CameraAccessException;
-
- /**
* <p>Create a new camera capture session by providing the target output set of Surfaces to the
* camera device.</p>
*
@@ -276,68 +267,6 @@
throws CameraAccessException;
/**
- * <p>Submit a request for an image to be captured by this CameraDevice.</p>
- *
- * @deprecated Use {@link CameraCaptureSession#capture} instead
- * @hide
- */
- @Deprecated
- public abstract int capture(CaptureRequest request, CaptureListener listener, Handler handler)
- throws CameraAccessException;
-
- /**
- * Submit a list of requests to be captured in sequence as a burst.
- *
- * @deprecated Use {@link CameraCaptureSession#captureBurst} instead
- * @hide
- */
- @Deprecated
- public abstract int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
- Handler handler) throws CameraAccessException;
-
- /**
- * Request endlessly repeating capture of images by this CameraDevice.
- *
- * @deprecated Use {@link CameraCaptureSession#setRepeatingRequest} instead
- * @hide
- */
- @Deprecated
- public abstract int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
- Handler handler) throws CameraAccessException;
-
- /**
- * <p>Request endlessly repeating capture of a sequence of images by this
- * CameraDevice.</p>
- *
- * @deprecated Use {@link CameraCaptureSession#setRepeatingBurst} instead
- * @hide
- */
- @Deprecated
- public abstract int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
- Handler handler) throws CameraAccessException;
-
- /**
- * <p>Cancel any ongoing repeating capture set by either
- * {@link #setRepeatingRequest setRepeatingRequest} or
- * {@link #setRepeatingBurst}.
- *
- * @deprecated Use {@link CameraCaptureSession#stopRepeating} instead
- * @hide
- */
- @Deprecated
- public abstract void stopRepeating() throws CameraAccessException;
-
- /**
- * Flush all captures currently pending and in-progress as fast as
- * possible.
- *
- * @deprecated Use {@link CameraCaptureSession#abortCaptures} instead
- * @hide
- */
- @Deprecated
- public abstract void flush() throws CameraAccessException;
-
- /**
* Close the connection to this camera device as quickly as possible.
*
* <p>Immediately after this call, all calls to the camera device or active session interface
@@ -356,96 +285,6 @@
public abstract void close();
/**
- * <p>A listener for tracking the progress of a {@link CaptureRequest}
- * submitted to the camera device.</p>
- *
- * @deprecated Use {@link CameraCaptureSession.CaptureListener} instead
- * @hide
- */
- @Deprecated
- public static abstract class CaptureListener {
-
- /**
- * This constant is used to indicate that no images were captured for
- * the request.
- *
- * @hide
- */
- public static final int NO_FRAMES_CAPTURED = -1;
-
- /**
- * This method is called when the camera device has started capturing
- * the output image for the request, at the beginning of image exposure.
- *
- * @see android.media.MediaActionSound
- */
- public void onCaptureStarted(CameraDevice camera,
- CaptureRequest request, long timestamp) {
- // default empty implementation
- }
-
- /**
- * This method is called when some results from an image capture are
- * available.
- *
- * @hide
- */
- public void onCapturePartial(CameraDevice camera,
- CaptureRequest request, CaptureResult result) {
- // default empty implementation
- }
-
- /**
- * This method is called when an image capture makes partial forward progress; some
- * (but not all) results from an image capture are available.
- *
- */
- public void onCaptureProgressed(CameraDevice camera,
- CaptureRequest request, CaptureResult partialResult) {
- // default empty implementation
- }
-
- /**
- * This method is called when an image capture has fully completed and all the
- * result metadata is available.
- */
- public void onCaptureCompleted(CameraDevice camera,
- CaptureRequest request, TotalCaptureResult result) {
- // default empty implementation
- }
-
- /**
- * This method is called instead of {@link #onCaptureCompleted} when the
- * camera device failed to produce a {@link CaptureResult} for the
- * request.
- */
- public void onCaptureFailed(CameraDevice camera,
- CaptureRequest request, CaptureFailure failure) {
- // default empty implementation
- }
-
- /**
- * This method is called independently of the others in CaptureListener,
- * when a capture sequence finishes and all {@link CaptureResult}
- * or {@link CaptureFailure} for it have been returned via this listener.
- */
- public void onCaptureSequenceCompleted(CameraDevice camera,
- int sequenceId, long frameNumber) {
- // default empty implementation
- }
-
- /**
- * This method is called independently of the others in CaptureListener,
- * when a capture sequence aborts before any {@link CaptureResult}
- * or {@link CaptureFailure} for it have been returned via this listener.
- */
- public void onCaptureSequenceAborted(CameraDevice camera,
- int sequenceId) {
- // default empty implementation
- }
- }
-
- /**
* A listener for notifications about the state of a camera
* device.
*
@@ -542,40 +381,6 @@
public abstract void onOpened(CameraDevice camera); // Must implement
/**
- * The method called when a camera device has no outputs configured.
- *
- * @deprecated Use {@link #onOpened} instead.
- * @hide
- */
- @Deprecated
- public void onUnconfigured(CameraDevice camera) {
- // Default empty implementation
- }
-
- /**
- * The method called when a camera device begins processing
- * {@link CaptureRequest capture requests}.
- *
- * @deprecated Use {@link CameraCaptureSession.StateListener#onActive} instead.
- * @hide
- */
- @Deprecated
- public void onActive(CameraDevice camera) {
- // Default empty implementation
- }
-
- /**
- * The method called when a camera device is busy.
- *
- * @deprecated Use {@link CameraCaptureSession.StateListener#onConfigured} instead.
- * @hide
- */
- @Deprecated
- public void onBusy(CameraDevice camera) {
- // Default empty implementation
- }
-
- /**
* The method called when a camera device has been closed with
* {@link CameraDevice#close}.
*
@@ -591,18 +396,6 @@
}
/**
- * The method called when a camera device has finished processing all
- * submitted capture requests and has reached an idle state.
- *
- * @deprecated Use {@link CameraCaptureSession.StateListener#onReady} instead.
- * @hide
- */
- @Deprecated
- public void onIdle(CameraDevice camera) {
- // Default empty implementation
- }
-
- /**
* The method called when a camera device is no longer available for
* use.
*
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index f829f5e..a15028c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -103,7 +103,7 @@
* Use the same handler as the device's StateListener for all the internal coming events
*
* This ensures total ordering between CameraDevice.StateListener and
- * CameraDevice.CaptureListener events.
+ * CameraDeviceImpl.CaptureListener events.
*/
mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
/*name*/"seq");
@@ -141,7 +141,7 @@
checkNotClosed();
checkLegalToCapture();
- handler = checkHandler(handler);
+ handler = checkHandler(handler, listener);
if (VERBOSE) {
Log.v(TAG, "capture - request " + request + ", listener " + listener + " handler" +
@@ -164,7 +164,7 @@
checkNotClosed();
checkLegalToCapture();
- handler = checkHandler(handler);
+ handler = checkHandler(handler, listener);
if (VERBOSE) {
CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
@@ -186,7 +186,7 @@
checkNotClosed();
checkLegalToCapture();
- handler = checkHandler(handler);
+ handler = checkHandler(handler, listener);
if (VERBOSE) {
Log.v(TAG, "setRepeatingRequest - request " + request + ", listener " + listener +
@@ -209,7 +209,7 @@
checkNotClosed();
checkLegalToCapture();
- handler = checkHandler(handler);
+ handler = checkHandler(handler, listener);
if (VERBOSE) {
CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
@@ -261,9 +261,12 @@
* <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped.
* <p>
*
+ * <p>After this call completes, the session will not call any further methods on the camera
+ * device.</p>
+ *
* @see CameraCaptureSession#close
*/
- synchronized void replaceSessionClose(CameraCaptureSession other) {
+ synchronized void replaceSessionClose() {
/*
* In order for creating new sessions to be fast, the new session should be created
* before the old session is closed.
@@ -278,13 +281,17 @@
if (VERBOSE) Log.v(TAG, "replaceSessionClose");
- // #close was already called explicitly, keep going the slow route
- if (mClosed) {
- if (VERBOSE) Log.v(TAG, "replaceSessionClose - close was already called");
- return;
- }
-
+ // Set up fast shutdown. Possible alternative paths:
+ // - This session is active, so close() below starts the shutdown drain
+ // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener.
+ // - This session is already closed and has executed the idle drain listener, and
+ // configureOutputs(null) has already been called.
+ //
+ // Do not call configureOutputs(null) going forward, since it would race with the
+ // configuration for the new session. If it was already called, then we don't care, since it
+ // won't get called again.
mSkipUnconfigure = true;
+
close();
}
@@ -347,7 +354,7 @@
/**
* Forward callbacks from
- * CameraDevice.CaptureListener to the CameraCaptureSession.CaptureListener.
+ * CameraDeviceImpl.CaptureListener to the CameraCaptureSession.CaptureListener.
*
* <p>In particular, all calls are automatically split to go both to our own
* internal listener, and to the user-specified listener (by transparently posting
@@ -356,9 +363,9 @@
* <p>When a capture sequence finishes, update the pending checked sequences set.</p>
*/
@SuppressWarnings("deprecation")
- private CameraDevice.CaptureListener createCaptureListenerProxy(
+ private CameraDeviceImpl.CaptureListener createCaptureListenerProxy(
Handler handler, CaptureListener listener) {
- CameraDevice.CaptureListener localListener = new CameraDevice.CaptureListener() {
+ CameraDeviceImpl.CaptureListener localListener = new CameraDeviceImpl.CaptureListener() {
@Override
public void onCaptureSequenceCompleted(CameraDevice camera,
int sequenceId, long frameNumber) {
@@ -379,27 +386,30 @@
* - then forward the call to a handler
* - then finally invoke the destination method on the session listener object
*/
- Dispatchable<CaptureListener> userListenerSink;
- if (listener == null) { // OK: API allows the user to not specify a listener
- userListenerSink = new NullDispatcher<>();
- } else {
- userListenerSink = new InvokeDispatcher<>(listener);
+ if (listener == null) {
+ // OK: API allows the user to not specify a listener, and the handler may
+ // also be null in that case. Collapse whole dispatch chain to only call the local
+ // listener
+ return localListener;
}
- InvokeDispatcher<CameraDevice.CaptureListener> localSink =
+ InvokeDispatcher<CameraDeviceImpl.CaptureListener> localSink =
new InvokeDispatcher<>(localListener);
+
+ InvokeDispatcher<CaptureListener> userListenerSink =
+ new InvokeDispatcher<>(listener);
HandlerDispatcher<CaptureListener> handlerPassthrough =
new HandlerDispatcher<>(userListenerSink, handler);
- DuckTypingDispatcher<CameraDevice.CaptureListener, CaptureListener> duckToSession
+ DuckTypingDispatcher<CameraDeviceImpl.CaptureListener, CaptureListener> duckToSession
= new DuckTypingDispatcher<>(handlerPassthrough, CaptureListener.class);
- ArgumentReplacingDispatcher<CameraDevice.CaptureListener, CameraCaptureSessionImpl>
- replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
- /*argumentIndex*/0, this);
+ ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureListener, CameraCaptureSessionImpl>
+ replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
+ /*argumentIndex*/0, this);
- BroadcastDispatcher<CameraDevice.CaptureListener> broadcaster =
- new BroadcastDispatcher<CameraDevice.CaptureListener>(
- replaceDeviceWithSession,
- localSink);
+ BroadcastDispatcher<CameraDeviceImpl.CaptureListener> broadcaster =
+ new BroadcastDispatcher<CameraDeviceImpl.CaptureListener>(
+ replaceDeviceWithSession,
+ localSink);
return new ListenerProxies.DeviceCaptureListenerProxy(broadcaster);
}
@@ -415,10 +425,10 @@
* </ul>
* </p>
* */
- CameraDevice.StateListener getDeviceStateListener() {
+ CameraDeviceImpl.StateListenerKK getDeviceStateListener() {
final CameraCaptureSession session = this;
- return new CameraDevice.StateListener() {
+ return new CameraDeviceImpl.StateListenerKK() {
private boolean mBusy = false;
private boolean mActive = false;
@@ -596,6 +606,8 @@
*
* This operation is idempotent; a session will not be closed twice.
*/
+ if (VERBOSE) Log.v(TAG, "Session drain complete, skip unconfigure: " +
+ mSkipUnconfigure);
// Fast path: A new capture session has replaced this one; don't unconfigure.
if (mSkipUnconfigure) {
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 18b1202..71eb0e9 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -21,8 +21,10 @@
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.TotalCaptureResult;
@@ -47,7 +49,7 @@
/**
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
*/
-public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
+public class CameraDeviceImpl extends CameraDevice {
private final String TAG;
private final boolean DEBUG;
@@ -62,7 +64,7 @@
private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
private final StateListener mDeviceListener;
- private volatile StateListener mSessionStateListener;
+ private volatile StateListenerKK mSessionStateListener;
private final Handler mDeviceHandler;
private volatile boolean mClosing = false;
@@ -103,7 +105,7 @@
private final Runnable mCallOnOpened = new Runnable() {
@Override
public void run() {
- StateListener sessionListener = null;
+ StateListenerKK sessionListener = null;
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) return; // Camera already closed
@@ -119,7 +121,7 @@
private final Runnable mCallOnUnconfigured = new Runnable() {
@Override
public void run() {
- StateListener sessionListener = null;
+ StateListenerKK sessionListener = null;
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) return; // Camera already closed
@@ -128,14 +130,13 @@
if (sessionListener != null) {
sessionListener.onUnconfigured(CameraDeviceImpl.this);
}
- mDeviceListener.onUnconfigured(CameraDeviceImpl.this);
}
};
private final Runnable mCallOnActive = new Runnable() {
@Override
public void run() {
- StateListener sessionListener = null;
+ StateListenerKK sessionListener = null;
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) return; // Camera already closed
@@ -144,14 +145,13 @@
if (sessionListener != null) {
sessionListener.onActive(CameraDeviceImpl.this);
}
- mDeviceListener.onActive(CameraDeviceImpl.this);
}
};
private final Runnable mCallOnBusy = new Runnable() {
@Override
public void run() {
- StateListener sessionListener = null;
+ StateListenerKK sessionListener = null;
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) return; // Camera already closed
@@ -160,7 +160,6 @@
if (sessionListener != null) {
sessionListener.onBusy(CameraDeviceImpl.this);
}
- mDeviceListener.onBusy(CameraDeviceImpl.this);
}
};
@@ -172,7 +171,7 @@
if (mClosedOnce) {
throw new AssertionError("Don't post #onClosed more than once");
}
- StateListener sessionListener = null;
+ StateListenerKK sessionListener = null;
synchronized(mInterfaceLock) {
sessionListener = mSessionStateListener;
}
@@ -187,7 +186,7 @@
private final Runnable mCallOnIdle = new Runnable() {
@Override
public void run() {
- StateListener sessionListener = null;
+ StateListenerKK sessionListener = null;
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) return; // Camera already closed
@@ -196,14 +195,13 @@
if (sessionListener != null) {
sessionListener.onIdle(CameraDeviceImpl.this);
}
- mDeviceListener.onIdle(CameraDeviceImpl.this);
}
};
private final Runnable mCallOnDisconnected = new Runnable() {
@Override
public void run() {
- StateListener sessionListener = null;
+ StateListenerKK sessionListener = null;
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) return; // Camera already closed
@@ -313,7 +311,6 @@
return mCameraId;
}
- @Override
public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
// Treat a null input the same an empty list
if (outputs == null) {
@@ -390,7 +387,11 @@
checkIfCameraClosedOrInError();
- // TODO: we must be in UNCONFIGURED mode to begin with, or using another session
+ // Notify current session that it's going away, before starting camera operations
+ // After this call completes, the session is not allowed to call into CameraDeviceImpl
+ if (mCurrentSession != null) {
+ mCurrentSession.replaceSessionClose();
+ }
// TODO: dont block for this
boolean configureSuccess = true;
@@ -410,10 +411,6 @@
new CameraCaptureSessionImpl(outputs, listener, handler, this, mDeviceHandler,
configureSuccess);
- if (mCurrentSession != null) {
- mCurrentSession.replaceSessionClose(newSession);
- }
-
// TODO: wait until current session closes, then create the new session
mCurrentSession = newSession;
@@ -425,6 +422,15 @@
}
}
+ /**
+ * For use by backwards-compatibility code only.
+ */
+ public void setSessionListener(StateListenerKK sessionListener) {
+ synchronized(mInterfaceLock) {
+ mSessionStateListener = sessionListener;
+ }
+ }
+
@Override
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
@@ -449,7 +455,6 @@
}
}
- @Override
public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
throws CameraAccessException {
if (DEBUG) {
@@ -460,7 +465,6 @@
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()) {
@@ -543,9 +547,7 @@
// Need a valid handler, or current thread needs to have a looper, if
// listener is valid
- if (listener != null) {
- handler = checkHandler(handler);
- }
+ handler = checkHandler(handler, listener);
// Make sure that there all requests have at least 1 surface; all surfaces are non-null
for (CaptureRequest request : requestList) {
@@ -613,7 +615,6 @@
}
}
- @Override
public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Handler handler) throws CameraAccessException {
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
@@ -621,7 +622,6 @@
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()) {
@@ -630,7 +630,6 @@
return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
}
- @Override
public void stopRepeating() throws CameraAccessException {
synchronized(mInterfaceLock) {
@@ -681,7 +680,6 @@
}
}
- @Override
public void flush() throws CameraAccessException {
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
@@ -739,6 +737,133 @@
}
}
+ /**
+ * <p>A listener for tracking the progress of a {@link CaptureRequest}
+ * submitted to the camera device.</p>
+ *
+ */
+ public static abstract class CaptureListener {
+
+ /**
+ * This constant is used to indicate that no images were captured for
+ * the request.
+ *
+ * @hide
+ */
+ public static final int NO_FRAMES_CAPTURED = -1;
+
+ /**
+ * This method is called when the camera device has started capturing
+ * the output image for the request, at the beginning of image exposure.
+ *
+ * @see android.media.MediaActionSound
+ */
+ public void onCaptureStarted(CameraDevice camera,
+ CaptureRequest request, long timestamp) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called when some results from an image capture are
+ * available.
+ *
+ * @hide
+ */
+ public void onCapturePartial(CameraDevice camera,
+ CaptureRequest request, CaptureResult result) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called when an image capture makes partial forward progress; some
+ * (but not all) results from an image capture are available.
+ *
+ */
+ public void onCaptureProgressed(CameraDevice camera,
+ CaptureRequest request, CaptureResult partialResult) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called when an image capture has fully completed and all the
+ * result metadata is available.
+ */
+ public void onCaptureCompleted(CameraDevice camera,
+ CaptureRequest request, TotalCaptureResult result) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called instead of {@link #onCaptureCompleted} when the
+ * camera device failed to produce a {@link CaptureResult} for the
+ * request.
+ */
+ public void onCaptureFailed(CameraDevice camera,
+ CaptureRequest request, CaptureFailure failure) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called independently of the others in CaptureListener,
+ * when a capture sequence finishes and all {@link CaptureResult}
+ * or {@link CaptureFailure} for it have been returned via this listener.
+ */
+ public void onCaptureSequenceCompleted(CameraDevice camera,
+ int sequenceId, long frameNumber) {
+ // default empty implementation
+ }
+
+ /**
+ * This method is called independently of the others in CaptureListener,
+ * when a capture sequence aborts before any {@link CaptureResult}
+ * or {@link CaptureFailure} for it have been returned via this listener.
+ */
+ public void onCaptureSequenceAborted(CameraDevice camera,
+ int sequenceId) {
+ // default empty implementation
+ }
+ }
+
+ /**
+ * A listener for notifications about the state of a camera device, adding in the callbacks that
+ * were part of the earlier KK API design, but now only used internally.
+ */
+ public static abstract class StateListenerKK extends StateListener {
+ /**
+ * The method called when a camera device has no outputs configured.
+ *
+ */
+ public void onUnconfigured(CameraDevice camera) {
+ // Default empty implementation
+ }
+
+ /**
+ * The method called when a camera device begins processing
+ * {@link CaptureRequest capture requests}.
+ *
+ */
+ public void onActive(CameraDevice camera) {
+ // Default empty implementation
+ }
+
+ /**
+ * The method called when a camera device is busy.
+ *
+ */
+ public void onBusy(CameraDevice camera) {
+ // Default empty implementation
+ }
+
+ /**
+ * The method called when a camera device has finished processing all
+ * submitted capture requests and has reached an idle state.
+ *
+ */
+ public void onIdle(CameraDevice camera) {
+ // Default empty implementation
+ }
+ }
+
static class CaptureListenerHolder {
private final boolean mRepeating;
@@ -1155,6 +1280,18 @@
return handler;
}
+ /**
+ * Default handler management, conditional on there being a listener.
+ *
+ * <p>If the listener isn't null, check the handler, otherwise pass it through.</p>
+ */
+ static <T> Handler checkHandler(Handler handler, T listener) {
+ if (listener != null) {
+ return checkHandler(handler);
+ }
+ return handler;
+ }
+
private void checkIfCameraClosedOrInError() throws CameraAccessException {
if (mInError) {
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index dc71a06..febb015 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -67,6 +67,7 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
/**
* Implementation of camera metadata marshal/unmarshal across Binder to
@@ -227,6 +228,7 @@
private static final String CELLID_PROCESS = "CELLID";
private static final String GPS_PROCESS = "GPS";
+ private static final int FACE_LANDMARK_SIZE = 6;
private static String translateLocationProviderToProcess(final String provider) {
if (provider == null) {
@@ -347,7 +349,7 @@
// Check if key has been overridden to use a wrapper class on the java side.
GetCommand g = sGetCommandMap.get(key);
if (g != null) {
- return (T) g.getValue(this, key);
+ return g.getValue(this, key);
}
return getBase(key);
}
@@ -587,9 +589,71 @@
return availableFormats;
}
- private Face[] getFaces() {
- final int FACE_LANDMARK_SIZE = 6;
+ private boolean setFaces(Face[] faces) {
+ if (faces == null) {
+ return false;
+ }
+ int numFaces = faces.length;
+
+ // Detect if all faces are SIMPLE or not; count # of valid faces
+ boolean fullMode = true;
+ for (Face face : faces) {
+ if (face == null) {
+ numFaces--;
+ Log.w(TAG, "setFaces - null face detected, skipping");
+ continue;
+ }
+
+ if (face.getId() == Face.ID_UNSUPPORTED) {
+ fullMode = false;
+ }
+ }
+
+ Rect[] faceRectangles = new Rect[numFaces];
+ byte[] faceScores = new byte[numFaces];
+ int[] faceIds = null;
+ int[] faceLandmarks = null;
+
+ if (fullMode) {
+ faceIds = new int[numFaces];
+ faceLandmarks = new int[numFaces * FACE_LANDMARK_SIZE];
+ }
+
+ int i = 0;
+ for (Face face : faces) {
+ if (face == null) {
+ continue;
+ }
+
+ faceRectangles[i] = face.getBounds();
+ faceScores[i] = (byte)face.getScore();
+
+ if (fullMode) {
+ faceIds[i] = face.getId();
+
+ int j = 0;
+
+ faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().x;
+ faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().y;
+ faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().x;
+ faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().y;
+ faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().x;
+ faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().y;
+ }
+
+ i++;
+ }
+
+ set(CaptureResult.STATISTICS_FACE_RECTANGLES, faceRectangles);
+ set(CaptureResult.STATISTICS_FACE_IDS, faceIds);
+ set(CaptureResult.STATISTICS_FACE_LANDMARKS, faceLandmarks);
+ set(CaptureResult.STATISTICS_FACE_SCORES, faceScores);
+
+ return true;
+ }
+
+ private Face[] getFaces() {
Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE);
if (faceDetectMode == null) {
Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
@@ -653,9 +717,12 @@
if (faceScores[i] <= Face.SCORE_MAX &&
faceScores[i] >= Face.SCORE_MIN &&
faceIds[i] >= 0) {
- Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]);
- Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]);
- Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]);
+ Point leftEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE],
+ faceLandmarks[i*FACE_LANDMARK_SIZE+1]);
+ Point rightEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+2],
+ faceLandmarks[i*FACE_LANDMARK_SIZE+3]);
+ Point mouth = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+4],
+ faceLandmarks[i*FACE_LANDMARK_SIZE+5]);
Face face = new Face(faceRectangles[i], faceScores[i], faceIds[i],
leftEye, rightEye, mouth);
faceList.add(face);
@@ -865,6 +932,13 @@
metadata.setFaceRectangles((Rect[]) value);
}
});
+ sSetCommandMap.put(CaptureResult.STATISTICS_FACES.getNativeKey(),
+ new SetCommand() {
+ @Override
+ public <T> void setValue(CameraMetadataNative metadata, T value) {
+ metadata.setFaces((Face[])value);
+ }
+ });
sSetCommandMap.put(CaptureRequest.TONEMAP_CURVE.getNativeKey(), new SetCommand() {
@Override
public <T> void setValue(CameraMetadataNative metadata, T value) {
diff --git a/core/java/android/hardware/camera2/impl/ListenerProxies.java b/core/java/android/hardware/camera2/impl/ListenerProxies.java
index ab9a4d5..f44f9ad 100644
--- a/core/java/android/hardware/camera2/impl/ListenerProxies.java
+++ b/core/java/android/hardware/camera2/impl/ListenerProxies.java
@@ -36,13 +36,13 @@
// TODO: replace with codegen
- public static class DeviceStateListenerProxy extends CameraDevice.StateListener {
- private final MethodNameInvoker<CameraDevice.StateListener> mProxy;
+ public static class DeviceStateListenerProxy extends CameraDeviceImpl.StateListenerKK {
+ private final MethodNameInvoker<CameraDeviceImpl.StateListenerKK> mProxy;
public DeviceStateListenerProxy(
- Dispatchable<CameraDevice.StateListener> dispatchTarget) {
+ Dispatchable<CameraDeviceImpl.StateListenerKK> dispatchTarget) {
dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDevice.StateListener.class);
+ mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.StateListenerKK.class);
}
@Override
@@ -87,13 +87,13 @@
}
@SuppressWarnings("deprecation")
- public static class DeviceCaptureListenerProxy extends CameraDevice.CaptureListener {
- private final MethodNameInvoker<CameraDevice.CaptureListener> mProxy;
+ public static class DeviceCaptureListenerProxy extends CameraDeviceImpl.CaptureListener {
+ private final MethodNameInvoker<CameraDeviceImpl.CaptureListener> mProxy;
public DeviceCaptureListenerProxy(
- Dispatchable<CameraDevice.CaptureListener> dispatchTarget) {
+ Dispatchable<CameraDeviceImpl.CaptureListener> dispatchTarget) {
dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
- mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDevice.CaptureListener.class);
+ mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.CaptureListener.class);
}
@Override
diff --git a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
new file mode 100644
index 0000000..1470b70
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
@@ -0,0 +1,231 @@
+/*
+ * 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.graphics.Rect;
+import android.hardware.Camera;
+import android.hardware.Camera.FaceDetectionListener;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.legacy.ParameterUtils.ZoomData;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.params.Face;
+import android.hardware.camera2.utils.ListUtils;
+import android.hardware.camera2.utils.ParamsUtils;
+import android.util.Log;
+import android.util.Size;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static android.hardware.camera2.CaptureRequest.*;
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Map legacy face detect callbacks into face detection results.
+ */
+@SuppressWarnings("deprecation")
+public class LegacyFaceDetectMapper {
+ private static String TAG = "LegacyFaceDetectMapper";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private final Camera mCamera;
+ private final boolean mFaceDetectSupported;
+ private boolean mFaceDetectEnabled = false;
+
+ private final Object mLock = new Object();
+ private Camera.Face[] mFaces;
+ private Camera.Face[] mFacesPrev;
+ /**
+ * Instantiate a new face detect mapper.
+ *
+ * @param camera a non-{@code null} camera1 device
+ * @param characteristics a non-{@code null} camera characteristics for that camera1
+ *
+ * @throws NullPointerException if any of the args were {@code null}
+ */
+ public LegacyFaceDetectMapper(Camera camera, CameraCharacteristics characteristics) {
+ mCamera = checkNotNull(camera, "camera must not be null");
+ checkNotNull(characteristics, "characteristics must not be null");
+
+ mFaceDetectSupported = ArrayUtils.contains(
+ characteristics.get(
+ CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES),
+ STATISTICS_FACE_DETECT_MODE_SIMPLE);
+
+ if (!mFaceDetectSupported) {
+ return;
+ }
+
+ mCamera.setFaceDetectionListener(new FaceDetectionListener() {
+
+ @Override
+ public void onFaceDetection(Camera.Face[] faces, Camera camera) {
+ int lengthFaces = faces == null ? 0 : faces.length;
+ synchronized (mLock) {
+ if (mFaceDetectEnabled) {
+ mFaces = faces;
+ } else if (lengthFaces > 0) {
+ // stopFaceDetectMode could race against the requests, print a debug log
+ Log.d(TAG,
+ "onFaceDetection - Ignored some incoming faces since" +
+ "face detection was disabled");
+ }
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "onFaceDetection - read " + lengthFaces + " faces");
+ }
+ }
+ });
+ }
+
+ /**
+ * Process the face detect mode from the capture request into an api1 face detect toggle.
+ *
+ * <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 faces detected 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 processFaceDetectMode(CaptureRequest captureRequest,
+ Camera.Parameters parameters) {
+ checkNotNull(captureRequest, "captureRequest must not be null");
+
+ /*
+ * statistics.faceDetectMode
+ */
+ int fdMode = ParamsUtils.getOrDefault(captureRequest, STATISTICS_FACE_DETECT_MODE,
+ STATISTICS_FACE_DETECT_MODE_OFF);
+
+ if (fdMode != STATISTICS_FACE_DETECT_MODE_OFF && !mFaceDetectSupported) {
+ Log.w(TAG,
+ "processFaceDetectMode - Ignoring statistics.faceDetectMode; " +
+ "face detection is not available");
+ return;
+ }
+
+ // Print some warnings out in case the values were wrong
+ switch (fdMode) {
+ case STATISTICS_FACE_DETECT_MODE_OFF:
+ case STATISTICS_FACE_DETECT_MODE_SIMPLE:
+ break;
+ case STATISTICS_FACE_DETECT_MODE_FULL:
+ Log.w(TAG,
+ "processFaceDetectMode - statistics.faceDetectMode == FULL unsupported, " +
+ "downgrading to SIMPLE");
+ break;
+ default:
+ Log.w(TAG, "processFaceDetectMode - ignoring unknown statistics.faceDetectMode = "
+ + fdMode);
+ return;
+ }
+
+ boolean enableFaceDetect = fdMode != STATISTICS_FACE_DETECT_MODE_OFF;
+ synchronized (mLock) {
+ // Enable/disable face detection if it's changed since last time
+ if (enableFaceDetect != mFaceDetectEnabled) {
+ if (enableFaceDetect) {
+ mCamera.startFaceDetection();
+
+ if (VERBOSE) {
+ Log.v(TAG, "processFaceDetectMode - start face detection");
+ }
+ } else {
+ mCamera.stopFaceDetection();
+
+ if (VERBOSE) {
+ Log.v(TAG, "processFaceDetectMode - stop face detection");
+ }
+
+ mFaces = null;
+ }
+
+ mFaceDetectEnabled = enableFaceDetect;
+ }
+ }
+ }
+
+ /**
+ * Update the {@code result} camera metadata map with the new value for the
+ * {@code statistics.faces} and {@code statistics.faceDetectMode}.
+ *
+ * <p>Face detect callbacks are processed in the background, and each call to
+ * {@link #mapResultFaces} will have the latest faces as reflected by the camera1 callbacks.</p>
+ *
+ * @param result a non-{@code null} result
+ * @param legacyRequest a non-{@code null} request (read-only)
+ */
+ public void mapResultFaces(CameraMetadataNative result, LegacyRequest legacyRequest) {
+ checkNotNull(result, "result must not be null");
+ checkNotNull(legacyRequest, "legacyRequest must not be null");
+
+ Camera.Face[] faces, previousFaces;
+ int fdMode;
+ synchronized (mLock) {
+ fdMode = mFaceDetectEnabled ?
+ STATISTICS_FACE_DETECT_MODE_SIMPLE : STATISTICS_FACE_DETECT_MODE_OFF;
+
+ if (mFaceDetectEnabled) {
+ faces = mFaces;
+ } else {
+ faces = null;
+ }
+
+ previousFaces = mFacesPrev;
+ mFacesPrev = faces;
+ }
+
+ CameraCharacteristics characteristics = legacyRequest.characteristics;
+ CaptureRequest request = legacyRequest.captureRequest;
+ Size previewSize = legacyRequest.previewSize;
+ Camera.Parameters params = legacyRequest.parameters;
+
+ Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
+ request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);
+
+ List<Face> convertedFaces = new ArrayList<>();
+ if (faces != null) {
+ for (Camera.Face face : faces) {
+ if (face != null) {
+ convertedFaces.add(
+ ParameterUtils.convertFaceFromLegacy(face, activeArray, zoomData));
+ } else {
+ Log.w(TAG, "mapResultFaces - read NULL face from camera1 device");
+ }
+ }
+ }
+
+ if (VERBOSE && previousFaces != faces) { // Log only in verbose and IF the faces changed
+ Log.v(TAG, "mapResultFaces - changed to " + ListUtils.listToString(convertedFaces));
+ }
+
+ result.set(CaptureResult.STATISTICS_FACES, convertedFaces.toArray(new Face[0]));
+ result.set(CaptureResult.STATISTICS_FACE_DETECT_MODE, fdMode);
+ }
+}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java
index e576b43..d0a3a3f 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java
@@ -247,7 +247,8 @@
// No action necessary. The callbacks will handle transitions.
break;
default:
- Log.w(TAG, "mapTriggers - ignoring unknown control.afTrigger = " + afTrigger);
+ Log.w(TAG, "processRequestTriggers - ignoring unknown control.afTrigger = "
+ + afTrigger);
}
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 711edf4..b05508b 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -204,6 +204,11 @@
mapSensor(m, p);
/*
+ * statistics.*
+ */
+ mapStatistics(m, p);
+
+ /*
* sync.*
*/
mapSync(m, p);
@@ -487,6 +492,18 @@
private static void mapControlOther(CameraMetadataNative m, Camera.Parameters p) {
/*
+ * android.control.availableVideoStabilizationModes
+ */
+ {
+ int stabModes[] = p.isVideoStabilizationSupported() ?
+ new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF,
+ CONTROL_VIDEO_STABILIZATION_MODE_ON } :
+ new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF };
+
+ m.set(CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, stabModes);
+ }
+
+ /*
* android.control.maxRegions
*/
final int AE = 0, AWB = 1, AF = 2;
@@ -742,6 +759,31 @@
m.set(SENSOR_INFO_PIXEL_ARRAY_SIZE, largestJpegSize);
}
+ private static void mapStatistics(CameraMetadataNative m, Parameters p) {
+ /*
+ * statistics.info.availableFaceDetectModes
+ */
+ int[] fdModes;
+
+ if (p.getMaxNumDetectedFaces() > 0) {
+ fdModes = new int[] {
+ STATISTICS_FACE_DETECT_MODE_OFF,
+ STATISTICS_FACE_DETECT_MODE_SIMPLE
+ // FULL is never-listed, since we have no way to query it statically
+ };
+ } else {
+ fdModes = new int[] {
+ STATISTICS_FACE_DETECT_MODE_OFF
+ };
+ }
+ m.set(STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, fdModes);
+
+ /*
+ * statistics.info.maxFaceCount
+ */
+ m.set(STATISTICS_INFO_MAX_FACE_COUNT, p.getMaxNumDetectedFaces());
+ }
+
private static void mapSync(CameraMetadataNative m, Parameters p) {
/*
* sync.maxLatency
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
index a6fe035c..20f3fd2 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
@@ -150,10 +150,8 @@
if (supported) {
params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
- params.setRecordingHint(false);
} else {
Log.w(TAG, "Unsupported FPS range set [" + legacyFps[0] + "," + legacyFps[1] + "]");
- params.setRecordingHint(true);
}
}
@@ -248,6 +246,18 @@
// TODO: Don't add control.awbLock to availableRequestKeys if it's not supported
}
+ // control.videoStabilizationMode
+ {
+ Integer stabMode = getIfSupported(request, CONTROL_VIDEO_STABILIZATION_MODE,
+ /*defaultValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF,
+ params.isVideoStabilizationSupported(),
+ /*allowedValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF);
+
+ if (stabMode != null) {
+ params.setVideoStabilization(stabMode == CONTROL_VIDEO_STABILIZATION_MODE_ON);
+ }
+ }
+
// lens.focusDistance
{
boolean infinityFocusSupported =
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index 9eff943..a2487f4 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -35,6 +35,9 @@
import java.util.List;
import static com.android.internal.util.Preconditions.*;
+import static android.hardware.camera2.CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_OFF;
+import static android.hardware.camera2.CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_ON;
+import static android.hardware.camera2.CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE;
import static android.hardware.camera2.CaptureResult.*;
/**
@@ -142,7 +145,6 @@
*/
mapAwb(result, /*out*/params);
-
/*
* control.mode
*/
@@ -171,7 +173,6 @@
}
}
-
/*
* control.effectMode
*/
@@ -187,6 +188,15 @@
}
}
+ // control.videoStabilizationMode
+ {
+ int stabMode =
+ (params.isVideoStabilizationSupported() && params.getVideoStabilization()) ?
+ CONTROL_VIDEO_STABILIZATION_MODE_ON :
+ CONTROL_VIDEO_STABILIZATION_MODE_OFF;
+ result.set(CONTROL_VIDEO_STABILIZATION_MODE, stabMode);
+ }
+
/*
* flash
*/
diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
index efd12f2..385f844 100644
--- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java
+++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
@@ -43,6 +43,7 @@
/**
* Various utilities for dealing with camera API1 parameters.
*/
+@SuppressWarnings("deprecation")
public class ParameterUtils {
/** Upper/left minimal point of a normalized rectangle */
public static final int NORMALIZED_RECTANGLE_MIN = -1000;
@@ -164,19 +165,23 @@
* <p>If the score is out of range of {@value Face#SCORE_MIN}, {@value Face#SCORE_MAX},
* the score is clipped first and a warning is printed to logcat.</p>
*
+ * <p>If the id is negative, the id is changed to 0 and a warning is printed to
+ * logcat.</p>
+ *
* <p>All other parameters are passed-through as-is.</p>
*
* @return a new face with the optional features set
*/
public Face toFace(
int id, Point leftEyePosition, Point rightEyePosition, Point mouthPosition) {
+ int idSafe = clipLower(id, /*lo*/0, rect, "id");
int score = clip(weight,
Face.SCORE_MIN,
Face.SCORE_MAX,
rect,
"score");
- return new Face(rect, score, id, leftEyePosition, rightEyePosition, mouthPosition);
+ return new Face(rect, score, idSafe, leftEyePosition, rightEyePosition, mouthPosition);
}
/**
@@ -861,6 +866,61 @@
/*usePreviewCrop*/true);
}
+ /**
+ * Convert an api1 face into an active-array based api2 face.
+ *
+ * <p>Out-of-ranges scores and ids will be clipped to be within range (with a warning).</p>
+ *
+ * @param face a non-{@code null} api1 face
+ * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
+ * @param zoomData the calculated zoom data corresponding to this request
+ *
+ * @return a non-{@code null} api2 face
+ *
+ * @throws NullPointerException if the {@code face} was {@code null}
+ */
+ public static Face convertFaceFromLegacy(Camera.Face face, Rect activeArray,
+ ZoomData zoomData) {
+ checkNotNull(face, "face must not be null");
+
+ Face api2Face;
+
+ Camera.Area fakeArea = new Camera.Area(face.rect, /*weight*/1);
+
+ WeightedRectangle faceRect =
+ convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, fakeArea);
+
+ Point leftEye = face.leftEye, rightEye = face.rightEye, mouth = face.mouth;
+ if (leftEye != null && rightEye != null && mouth != null) {
+ leftEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
+ leftEye, /*usePreviewCrop*/true);
+ rightEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
+ leftEye, /*usePreviewCrop*/true);
+ mouth = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
+ leftEye, /*usePreviewCrop*/true);
+
+ api2Face = faceRect.toFace(face.id, leftEye, rightEye, mouth);
+ } else {
+ api2Face = faceRect.toFace();
+ }
+
+ return api2Face;
+ }
+
+ private static Point convertCameraPointToActiveArrayPoint(
+ Rect activeArray, ZoomData zoomData, Point point, boolean usePreviewCrop) {
+ Rect pointedRect = new Rect(point.x, point.y, point.x, point.y);
+ Camera.Area pointedArea = new Area(pointedRect, /*weight*/1);
+
+ WeightedRectangle adjustedRect =
+ convertCameraAreaToActiveArrayRectangle(activeArray,
+ zoomData, pointedArea, usePreviewCrop);
+
+ Point transformedPoint = new Point(adjustedRect.rect.left, adjustedRect.rect.top);
+
+ return transformedPoint;
+ }
+
private static WeightedRectangle convertCameraAreaToActiveArrayRectangle(
Rect activeArray, ZoomData zoomData, Camera.Area area, boolean usePreviewCrop) {
Rect previewCrop = zoomData.previewCrop;
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index c556c32..ec233da7 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -39,7 +39,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import static com.android.internal.util.Preconditions.*;
@@ -55,18 +54,23 @@
* - An {@link CameraDeviceState} state machine that manages the callbacks for various operations.
* </p>
*/
+@SuppressWarnings("deprecation")
public class RequestThreadManager {
private final String TAG;
private final int mCameraId;
private final RequestHandlerThread mRequestThread;
private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
+ // For slightly more spammy messages that will get repeated every frame
+ private static final boolean VERBOSE =
+ Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.VERBOSE);
private final Camera mCamera;
private final CameraCharacteristics mCharacteristics;
private final CameraDeviceState mDeviceState;
private final CaptureCollector mCaptureCollector;
private final LegacyFocusStateMapper mFocusStateMapper;
+ private final LegacyFaceDetectMapper mFaceDetectMapper;
private static final int MSG_CONFIGURE_OUTPUTS = 1;
private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2;
@@ -219,6 +223,9 @@
};
private void stopPreview() {
+ if (VERBOSE) {
+ Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning);
+ }
if (mPreviewRunning) {
mCamera.stopPreview();
mPreviewRunning = false;
@@ -226,14 +233,18 @@
}
private void startPreview() {
+ if (VERBOSE) {
+ Log.v(TAG, "startPreview - preview running? " + mPreviewRunning);
+ }
if (!mPreviewRunning) {
+ // XX: CameraClient:;startPreview is not getting called after a stop
mCamera.startPreview();
mPreviewRunning = true;
}
}
- private void doJpegCapture(RequestHolder request) throws IOException {
- if (DEBUG) Log.d(TAG, "doJpegCapture");
+ private void doJpegCapturePrepare(RequestHolder request) throws IOException {
+ if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning);
if (!mPreviewRunning) {
if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface");
@@ -242,11 +253,20 @@
mCamera.setPreviewTexture(mDummyTexture);
startPreview();
}
+ }
+
+ private void doJpegCapture(RequestHolder request) {
+ if (DEBUG) Log.d(TAG, "doJpegCapturePrepare");
+
mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
mPreviewRunning = false;
}
private void doPreviewCapture(RequestHolder request) throws IOException {
+ if (VERBOSE) {
+ Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning);
+ }
+
if (mPreviewRunning) {
return; // Already running
}
@@ -264,7 +284,20 @@
}
private void configureOutputs(Collection<Surface> outputs) throws IOException {
+ if (DEBUG) {
+ String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
+
+ Log.d(TAG, "configureOutputs with " + outputsStr);
+ }
+
stopPreview();
+ /*
+ * Try to release the previous preview's surface texture earlier if we end up
+ * using a different one; this also reduces the likelihood of getting into a deadlock
+ * when disconnecting from the old previous texture at a later time.
+ */
+ mCamera.setPreviewTexture(/*surfaceTexture*/null);
+
if (mGLThreadManager != null) {
mGLThreadManager.waitUntilStarted();
mGLThreadManager.ignoreNewFrames();
@@ -305,7 +338,6 @@
}
mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
- mParams.setRecordingHint(true);
if (mPreviewOutputs.size() > 0) {
List<Size> outputSizes = new ArrayList<>(outputs.size());
@@ -575,7 +607,6 @@
Log.e(TAG, "Interrupted while waiting for requests to complete.");
}
mDeviceState.setIdle();
- stopPreview();
break;
} else {
// Queue another capture if we did not get the last burst.
@@ -613,10 +644,6 @@
}
}
- // 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);
@@ -624,6 +651,8 @@
if (!success) {
Log.e(TAG, "Timed out while queueing capture request.");
}
+ // Starting the preview needs to happen before enabling
+ // face detection or auto focus
if (holder.hasPreviewTargets()) {
doPreviewCapture(holder);
}
@@ -635,12 +664,33 @@
Log.e(TAG, "Timed out waiting for prior requests to complete.");
}
mReceivedJpeg.close();
+ doJpegCapturePrepare(holder);
+ if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
+ // TODO: report error to CameraDevice
+ Log.e(TAG, "Hit timeout for jpeg callback!");
+ }
+ }
+
+ /*
+ * Do all the actions that require a preview to have been started
+ */
+
+ // Toggle face detection on/off
+ // - do this before AF to give AF a chance to use faces
+ mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams);
+
+ // 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);
+
+ if (holder.hasJpegTargets()) {
doJpegCapture(holder);
if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
// TODO: report error to CameraDevice
Log.e(TAG, "Hit timeout for jpeg callback!");
}
}
+
} catch (IOException e) {
// TODO: report error to CameraDevice
throw new IOError(e);
@@ -677,6 +727,8 @@
mLastRequest, timestampMutable.value);
// Update AF state
mFocusStateMapper.mapResultTriggers(result);
+ // Update detected faces list
+ mFaceDetectMapper.mapResultFaces(result, mLastRequest);
mDeviceState.setCaptureResult(holder, result);
}
@@ -731,6 +783,7 @@
TAG = name;
mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
+ mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
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 1efabb1..2e6b9ae 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -813,6 +813,7 @@
switch (format) {
case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
case HAL_PIXEL_FORMAT_BLOB:
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
return format;
case ImageFormat.JPEG:
throw new IllegalArgumentException(
@@ -843,12 +844,6 @@
* @throws IllegalArgumentException if the format was not user-defined
*/
static int checkArgumentFormat(int format) {
- // TODO: remove this hack , CTS shouldn't have been using internal constants
- if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
- Log.w(TAG, "RAW_OPAQUE is not yet a published format; allowing it anyway");
- return format;
- }
-
if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
throw new IllegalArgumentException(String.format(
"format 0x%x was not defined in either ImageFormat or PixelFormat", format));
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index d4e6df5..51b7229 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -96,11 +96,9 @@
* windows on the display and the system may mirror the contents of other displays
* onto it.
* </p><p>
- * Creating a public virtual display requires the
- * {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT}
- * or {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT} permission.
- * These permissions are reserved for use by system components and are not available to
- * third-party applications.
+ * Creating a public virtual display that isn't restricted to own-content only implicitly
+ * creates an auto-mirroring display. See {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} for
+ * restrictions on who is allowed to create an auto-mirroring display.
* </p>
*
* <h3>Private virtual displays</h3>
@@ -108,6 +106,8 @@
* When this flag is not set, the virtual display is private as defined by the
* {@link Display#FLAG_PRIVATE} display flag.
* </p>
+ *
+ * <p>
* A private virtual display belongs to the application that created it.
* Only the a owner of a private virtual display is allowed to place windows upon it.
* The private virtual display also does not participate in display mirroring: it will
@@ -115,10 +115,11 @@
* be mirrored elsewhere. More precisely, the only processes that are allowed to
* enumerate or interact with the private display are those that have the same UID as the
* application that originally created the private virtual display.
- * </p>
+ * </p>
*
* @see #createVirtualDisplay
* @see #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ * @see #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
*/
public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1 << 0;
@@ -187,29 +188,51 @@
* will be blanked instead if it has no windows.
* </p>
*
+ * <p>
+ * This flag is mutually exclusive with {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. If both
+ * flags are specified then the own-content only behavior will be applied.
+ * </p>
+ *
+ * <p>
+ * This behavior of this flag is implied whenever neither {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}
+ * nor {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} have been set. This flag is only required to
+ * override the default behavior when creating a public display.
+ * </p>
+ *
* @see #createVirtualDisplay
*/
public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 1 << 3;
/**
- * Virtual display flag: Indicates that the display is being created for
- * the purpose of screen sharing. This implies
- * VIRTUAL_DISPLAY_FLAG_PRIVATE. Other flags are not allowed (especially
- * not VIRTUAL_DISPLAY_FLAG_PUBLIC or PRESENTATION).
+ * Virtual display flag: Allows content to be mirrored on private displays when no content is
+ * being shown.
*
* <p>
- * Requires screen share permission for use.
+ * This flag is mutually exclusive with {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
+ * If both flags are specified then the own-content only behavior will be applied.
* </p>
*
* <p>
- * While a display of this type exists, the system will show some sort of
- * notification to the user indicating that the screen is being shared.
+ * The behavior of this flag is implied whenever {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC} is set
+ * and {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY} has not been set. This flag is only
+ * required to override the default behavior when creating a private display.
+ * </p>
+ *
+ * <p>
+ * Creating an auto-mirroing virtual display requires the
+ * {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT}
+ * or {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT} permission.
+ * These permissions are reserved for use by system components and are not available to
+ * third-party applications.
+ *
+ * Alternatively, an appropriate {@link MediaProjection} may be used to create an
+ * auto-mirroring virtual display.
* </p>
*
* @see #createVirtualDisplay
*/
- public static final int VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE = 1 << 4;
+ public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 1 << 4;
/** @hide */
public DisplayManager(Context context) {
@@ -489,7 +512,7 @@
* @param flags A combination of virtual display flags:
* {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION},
* {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
- * or {@link #VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE}.
+ * or {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
* @param callbacks Callbacks to call when the state of the {@link VirtualDisplay} changes
* @param handler The handler on which the listener should be invoked, or null
* if the listener should be invoked on the calling thread's looper.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 04e6227..3087506 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -864,15 +864,6 @@
}
/**
- * Kept during L development to simplify updating unbundled apps.
- * TODO: Remove after 2014-08-04
- * @hide
- */
- public String getBadgedLabelForUser(String label, UserHandle user) {
- return (String) getBadgedLabelForUser((CharSequence) label, user);
- }
-
- /**
* If the target user is a managed profile of the calling user or the caller
* is itself a managed profile, then this returns a drawable to use as a small
* icon to include in a view to distinguish it from the original icon.
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 910f862..a410aa9 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -386,9 +386,6 @@
@Override
public void scale(float sx, float sy) {
- // TODO: remove
- if (sx > 1000000 || sy > 1000000) throw new IllegalArgumentException("invalid scales passed " + sx + ", " + sy);
-
nScale(mRenderer, sx, sy);
}
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index eee4973..099f153 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -598,9 +598,6 @@
* @see #getScaleX()
*/
public boolean setScaleX(float scaleX) {
- if (scaleX > 1000000) {
- throw new IllegalArgumentException("Invalid scale: " + scaleX);
- }
return nSetScaleX(mNativeRenderNode, scaleX);
}
@@ -622,9 +619,6 @@
* @see #getScaleY()
*/
public boolean setScaleY(float scaleY) {
- if (scaleY > 1000000) {
- throw new IllegalArgumentException("Invalid scale: " + scaleY);
- }
return nSetScaleY(mNativeRenderNode, scaleY);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 129476c..60ef693 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3996,6 +3996,22 @@
return super.onGenericMotionEvent(event);
}
+ /**
+ * Initiate a fling with the given velocity.
+ *
+ * <p>Applications can use this method to manually initiate a fling as if the user
+ * initiated it via touch interaction.</p>
+ *
+ * @param velocityY Vertical velocity in pixels per second
+ */
+ public void fling(int velocityY) {
+ if (mFlingRunnable == null) {
+ mFlingRunnable = new FlingRunnable();
+ }
+ reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
+ mFlingRunnable.start(-velocityY);
+ }
+
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
return ((nestedScrollAxes & SCROLL_AXIS_VERTICAL) != 0);
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 546cc5f..f1aaa4d 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -17,6 +17,7 @@
package android.widget;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.os.Handler;
@@ -55,7 +56,7 @@
* <p>
* Functions like show() and hide() have no effect when MediaController
* is created in an xml layout.
- *
+ *
* MediaController will hide and
* show the buttons according to these rules:
* <ul>
@@ -70,32 +71,34 @@
*/
public class MediaController extends FrameLayout {
- private MediaPlayerControl mPlayer;
- private Context mContext;
- private View mAnchor;
- private View mRoot;
- private WindowManager mWindowManager;
- private Window mWindow;
- private View mDecor;
+ private MediaPlayerControl mPlayer;
+ private Context mContext;
+ private View mAnchor;
+ private View mRoot;
+ private WindowManager mWindowManager;
+ private Window mWindow;
+ private View mDecor;
private WindowManager.LayoutParams mDecorLayoutParams;
- private ProgressBar mProgress;
- private TextView mEndTime, mCurrentTime;
- private boolean mShowing;
- private boolean mDragging;
- private static final int sDefaultTimeout = 3000;
- private static final int FADE_OUT = 1;
- private static final int SHOW_PROGRESS = 2;
- private boolean mUseFastForward;
- private boolean mFromXml;
- private boolean mListenersSet;
+ private ProgressBar mProgress;
+ private TextView mEndTime, mCurrentTime;
+ private boolean mShowing;
+ private boolean mDragging;
+ private static final int sDefaultTimeout = 3000;
+ private static final int FADE_OUT = 1;
+ private static final int SHOW_PROGRESS = 2;
+ private boolean mUseFastForward;
+ private boolean mFromXml;
+ private boolean mListenersSet;
private View.OnClickListener mNextListener, mPrevListener;
- StringBuilder mFormatBuilder;
- Formatter mFormatter;
- private ImageButton mPauseButton;
- private ImageButton mFfwdButton;
- private ImageButton mRewButton;
- private ImageButton mNextButton;
- private ImageButton mPrevButton;
+ StringBuilder mFormatBuilder;
+ Formatter mFormatter;
+ private ImageButton mPauseButton;
+ private ImageButton mFfwdButton;
+ private ImageButton mRewButton;
+ private ImageButton mNextButton;
+ private ImageButton mPrevButton;
+ private CharSequence mPlayDescription;
+ private CharSequence mPauseDescription;
public MediaController(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -132,7 +135,7 @@
mDecor.setOnTouchListener(mTouchListener);
mWindow.setContentView(this);
mWindow.setBackgroundDrawableResource(android.R.color.transparent);
-
+
// While the media controller is up, the volume control keys should
// affect the media stream type
mWindow.setVolumeControlStream(AudioManager.STREAM_MUSIC);
@@ -201,7 +204,7 @@
return false;
}
};
-
+
public void setMediaPlayer(MediaPlayerControl player) {
mPlayer = player;
updatePausePlay();
@@ -249,6 +252,11 @@
}
private void initControllerView(View v) {
+ Resources res = mContext.getResources();
+ mPlayDescription = res
+ .getText(com.android.internal.R.string.lockscreen_transport_play_description);
+ mPauseDescription = res
+ .getText(com.android.internal.R.string.lockscreen_transport_pause_description);
mPauseButton = (ImageButton) v.findViewById(com.android.internal.R.id.pause);
if (mPauseButton != null) {
mPauseButton.requestFocus();
@@ -271,7 +279,7 @@
}
}
- // By default these are hidden. They will be enabled when setPrevNextListeners() is called
+ // By default these are hidden. They will be enabled when setPrevNextListeners() is called
mNextButton = (ImageButton) v.findViewById(com.android.internal.R.id.next);
if (mNextButton != null && !mFromXml && !mListenersSet) {
mNextButton.setVisibility(View.GONE);
@@ -328,7 +336,7 @@
// the buttons.
}
}
-
+
/**
* Show the controller on screen. It will go away
* automatically after 'timeout' milliseconds of inactivity.
@@ -347,7 +355,7 @@
mShowing = true;
}
updatePausePlay();
-
+
// cause the progress bar to be updated even if mShowing
// was already true. This happens, for example, if we're
// paused with the progress bar showing the user hits play.
@@ -359,7 +367,7 @@
mHandler.sendMessageDelayed(msg, timeout);
}
}
-
+
public boolean isShowing() {
return mShowing;
}
@@ -525,8 +533,10 @@
if (mPlayer.isPlaying()) {
mPauseButton.setImageResource(com.android.internal.R.drawable.ic_media_pause);
+ mPauseButton.setContentDescription(mPauseDescription);
} else {
mPauseButton.setImageResource(com.android.internal.R.drawable.ic_media_play);
+ mPauseButton.setContentDescription(mPlayDescription);
}
}
@@ -668,7 +678,7 @@
if (mRoot != null) {
installPrevNextListeners();
-
+
if (mNextButton != null && !mFromXml) {
mNextButton.setVisibility(View.VISIBLE);
}
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 59ec6d3..8102c4a 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Parcel;
@@ -171,7 +172,10 @@
mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
/* Get the localized am/pm strings and use them in the spinner */
- mAmPmStrings = new DateFormatSymbols().getAmPmStrings();
+ final Resources res = context.getResources();
+ final String amText = res.getString(R.string.time_picker_am_label);
+ final String pmText = res.getString(R.string.time_picker_pm_label);
+ mAmPmStrings = new String[] {amText, pmText};
// am/pm
View amPmView = mDelegator.findViewById(R.id.amPm);
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 4e9a39f..523965c 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -134,6 +134,8 @@
mSelectHours = res.getString(R.string.select_hours);
mMinutePickerDescription = res.getString(R.string.minute_picker_description);
mSelectMinutes = res.getString(R.string.select_minutes);
+ mAmText = res.getString(R.string.time_picker_am_label);
+ mPmText = res.getString(R.string.time_picker_pm_label);
final int layoutResourceId = a.getResourceId(R.styleable.TimePicker_internalLayout,
R.layout.time_picker_holo);
@@ -182,10 +184,6 @@
R.id.radial_picker);
mDoneButton = (Button) mainView.findViewById(R.id.done_button);
- String[] amPmTexts = new DateFormatSymbols().getAmPmStrings();
- mAmText = amPmTexts[0];
- mPmText = amPmTexts[1];
-
setupListeners();
mAllowAutoAdvance = true;
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 97e1102..d8dffe0 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -273,11 +273,15 @@
@Override
public int finishBackup() {
- if (DEBUG) Log.v(TAG, "finishBackup()");
+ if (DEBUG) Log.v(TAG, "finishBackup() of " + mFullTargetPackage);
+ return tearDownFullBackup();
+ }
+
+ // ------------------------------------------------------------------------------------
+ // Full backup handling
+
+ private int tearDownFullBackup() {
if (mSocket != null) {
- if (DEBUG) {
- Log.v(TAG, "Concluding full backup of " + mFullTargetPackage);
- }
try {
mFullBackupOutputStream.flush();
mFullBackupOutputStream.close();
@@ -286,7 +290,7 @@
mSocket.close();
} catch (IOException e) {
if (DEBUG) {
- Log.w(TAG, "Exception caught in finishBackup()", e);
+ Log.w(TAG, "Exception caught in tearDownFullBackup()", e);
}
return TRANSPORT_ERROR;
} finally {
@@ -296,8 +300,9 @@
return TRANSPORT_OK;
}
- // ------------------------------------------------------------------------------------
- // Full backup handling
+ private File tarballFile(String pkgName) {
+ return new File(mCurrentSetFullDir, pkgName);
+ }
@Override
public long requestFullBackupTime() {
@@ -329,7 +334,7 @@
mFullTargetPackage = targetPackage.packageName;
FileOutputStream tarstream;
try {
- File tarball = new File(mCurrentSetFullDir, mFullTargetPackage);
+ File tarball = tarballFile(mFullTargetPackage);
tarstream = new FileOutputStream(tarball);
} catch (FileNotFoundException e) {
return TRANSPORT_ERROR;
@@ -368,6 +373,19 @@
return TRANSPORT_OK;
}
+ // For now we can't roll back, so just tear everything down.
+ @Override
+ public void cancelFullBackup() {
+ if (DEBUG) {
+ Log.i(TAG, "Canceling full backup of " + mFullTargetPackage);
+ }
+ File archive = tarballFile(mFullTargetPackage);
+ tearDownFullBackup();
+ if (archive.exists()) {
+ archive.delete();
+ }
+ }
+
// ------------------------------------------------------------------------------------
// Restore handling
static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 };
diff --git a/core/res/res/color/btn_default_material_dark.xml b/core/res/res/color/btn_default_material_dark.xml
index 59555ae..7c904cd 100644
--- a/core/res/res/color/btn_default_material_dark.xml
+++ b/core/res/res/color/btn_default_material_dark.xml
@@ -15,6 +15,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:alpha="0.5" android:color="@color/button_material_dark"/>
+ <item android:state_enabled="false"
+ android:alpha="@dimen/disabled_alpha_material"
+ android:color="@color/button_material_dark"/>
<item android:color="@color/button_material_dark"/>
</selector>
diff --git a/core/res/res/color/btn_default_material_light.xml b/core/res/res/color/btn_default_material_light.xml
index 6511a22..738f9ad 100644
--- a/core/res/res/color/btn_default_material_light.xml
+++ b/core/res/res/color/btn_default_material_light.xml
@@ -15,6 +15,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:alpha="0.5" android:color="@color/button_material_light"/>
+ <item android:state_enabled="false"
+ android:alpha="@dimen/disabled_alpha_material"
+ android:color="@color/button_material_light"/>
<item android:color="@color/button_material_light"/>
</selector>
diff --git a/core/res/res/color/primary_text_disable_only_material_dark.xml b/core/res/res/color/primary_text_disable_only_material_dark.xml
index cf7acaa..cdae790 100644
--- a/core/res/res/color/primary_text_disable_only_material_dark.xml
+++ b/core/res/res/color/primary_text_disable_only_material_dark.xml
@@ -15,6 +15,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:alpha="0.5" android:color="@android:color/bright_foreground_material_dark"/>
- <item android:color="@android:color/bright_foreground_material_dark"/>
+ <item android:state_enabled="false"
+ android:alpha="@dimen/disabled_alpha_material"
+ android:color="@color/bright_foreground_material_dark"/>
+ <item android:color="@color/bright_foreground_material_dark"/>
</selector>
diff --git a/core/res/res/color/primary_text_disable_only_material_light.xml b/core/res/res/color/primary_text_disable_only_material_light.xml
index bf5d2c0..0bf14d0 100644
--- a/core/res/res/color/primary_text_disable_only_material_light.xml
+++ b/core/res/res/color/primary_text_disable_only_material_light.xml
@@ -15,6 +15,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:alpha="0.5" android:color="@android:color/bright_foreground_material_light"/>
- <item android:color="@android:color/bright_foreground_material_light"/>
+ <item android:state_enabled="false"
+ android:alpha="@dimen/disabled_alpha_material"
+ android:color="@color/bright_foreground_material_light"/>
+ <item android:color="@color/bright_foreground_material_light"/>
</selector>
diff --git a/core/res/res/color/primary_text_material_dark.xml b/core/res/res/color/primary_text_material_dark.xml
index caef9d0..6ad837b 100644
--- a/core/res/res/color/primary_text_material_dark.xml
+++ b/core/res/res/color/primary_text_material_dark.xml
@@ -15,6 +15,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:alpha="0.5" android:color="@color/primary_text_default_material_dark"/>
+ <item android:state_enabled="false"
+ android:alpha="@dimen/disabled_alpha_material"
+ android:color="@color/primary_text_default_material_dark"/>
<item android:color="@color/primary_text_default_material_dark"/>
</selector>
diff --git a/core/res/res/color/primary_text_material_light.xml b/core/res/res/color/primary_text_material_light.xml
index 81a593b..4c19e12 100644
--- a/core/res/res/color/primary_text_material_light.xml
+++ b/core/res/res/color/primary_text_material_light.xml
@@ -15,6 +15,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:alpha="0.5" android:color="@color/primary_text_default_material_light"/>
+ <item android:state_enabled="false"
+ android:alpha="@dimen/disabled_alpha_material"
+ android:color="@color/primary_text_default_material_light"/>
<item android:color="@color/primary_text_default_material_light"/>
</selector>
diff --git a/core/res/res/drawable-hdpi/cab_background_bottom_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/cab_background_bottom_mtrl_alpha.9.png
new file mode 100644
index 0000000..92613b7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_background_bottom_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_background_top_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/cab_background_top_mtrl_alpha.9.png
new file mode 100644
index 0000000..e51ef280
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_background_bottom_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/cab_background_bottom_mtrl_alpha.9.png
new file mode 100644
index 0000000..df292a0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_background_bottom_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_background_top_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/cab_background_top_mtrl_alpha.9.png
new file mode 100644
index 0000000..ae8cccd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_bottom_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/cab_background_bottom_mtrl_alpha.9.png
new file mode 100644
index 0000000..9a4abd0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/cab_background_bottom_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_top_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/cab_background_top_mtrl_alpha.9.png
new file mode 100644
index 0000000..ed8d3411
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/cab_background_bottom_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/cab_background_bottom_mtrl_alpha.9.png
new file mode 100644
index 0000000..22bd8ce
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/cab_background_bottom_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/cab_background_top_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/cab_background_top_mtrl_alpha.9.png
new file mode 100644
index 0000000..1dd64b9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable/cab_background_bottom_material.xml b/core/res/res/drawable/cab_background_bottom_material.xml
new file mode 100644
index 0000000..cfbc854
--- /dev/null
+++ b/core/res/res/drawable/cab_background_bottom_material.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingMode="stack">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="?attr/colorBackground" />
+ </shape>
+ </item>
+ <item>
+ <nine-patch
+ android:src="@drawable/cab_background_bottom_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+</layer-list>
diff --git a/core/res/res/drawable/cab_background_top_material.xml b/core/res/res/drawable/cab_background_top_material.xml
new file mode 100644
index 0000000..09f7a61
--- /dev/null
+++ b/core/res/res/drawable/cab_background_top_material.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingMode="stack">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="?attr/colorBackground" />
+ </shape>
+ </item>
+ <item>
+ <nine-patch
+ android:src="@drawable/cab_background_top_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+</layer-list>
diff --git a/core/res/res/drawable/ic_corp_icon_badge.xml b/core/res/res/drawable/ic_corp_icon_badge.xml
index d20c431..834ae68 100644
--- a/core/res/res/drawable/ic_corp_icon_badge.xml
+++ b/core/res/res/drawable/ic_corp_icon_badge.xml
@@ -20,13 +20,11 @@
android:viewportHeight="64.0">
<path
- android:fillColor="#FF000000"
- android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"
- android:fillOpacity="0.2"/>
+ android:fillColor="#33000000"
+ android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"/>
<path
- android:fillColor="#FF000000"
- android:pathData="M49.0,49.5m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"
- android:fillOpacity="0.2"/>
+ android:fillColor="#33000000"
+ android:pathData="M49.0,49.5m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"/>
<path
android:pathData="M49.0,49.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"
android:fillColor="#FF5722"/>
diff --git a/core/res/res/drawable/switch_thumb_material_anim.xml b/core/res/res/drawable/switch_thumb_material_anim.xml
index 71f6cfd..30bc8882 100644
--- a/core/res/res/drawable/switch_thumb_material_anim.xml
+++ b/core/res/res/drawable/switch_thumb_material_anim.xml
@@ -31,8 +31,7 @@
android:src="@drawable/btn_switch_to_on_mtrl_00001"
android:gravity="center"
android:tintMode="multiply"
- android:tint="?attr/colorButtonNormal"
- android:alpha="?attr/disabledAlpha" />
+ android:tint="?attr/colorButtonNormal" />
</item>
<item
android:state_checked="true"
diff --git a/core/res/res/drawable/switch_track_material.xml b/core/res/res/drawable/switch_track_material.xml
index b400644..0728055 100644
--- a/core/res/res/drawable/switch_track_material.xml
+++ b/core/res/res/drawable/switch_track_material.xml
@@ -18,12 +18,7 @@
<item android:state_enabled="false" android:state_checked="true">
<nine-patch android:src="@drawable/switch_track_mtrl_alpha"
android:tint="?attr/colorControlActivated"
- android:alpha="0.15" />
- </item>
- <item android:state_enabled="false">
- <nine-patch android:src="@drawable/switch_track_mtrl_alpha"
- android:tint="?attr/colorButtonNormal"
- android:alpha="0.15" />
+ android:alpha="0.2" />
</item>
<item android:state_checked="true">
<nine-patch android:src="@drawable/switch_track_mtrl_alpha"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 771690e..736f1a9d2 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5208,13 +5208,13 @@
<attr name="name" />
<!-- The width a path stroke -->
<attr name="strokeWidth" format="float" />
- <!-- The opacity of a path stroke -->
+ <!-- The opacity of a path stroke @hide-->
<attr name="strokeOpacity" format="float" />
<!-- The color to stroke the path if not defined implies no stroke-->
<attr name="strokeColor" format="color" />
<!-- The color to fill the path if not defined implies no fill-->
<attr name="fillColor" format="color" />
- <!-- The level of opacity of the filled area of the path -->
+ <!-- The level of opacity of the filled area of the path @hide-->
<attr name="fillOpacity" format="float" />
<!-- The specification of the operations that define the path -->
<attr name="pathData" format="string" />
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index bae8e8d..c21dc59 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -68,4 +68,7 @@
<dimen name="control_padding_material">4dp</dimen>
<!-- Default rounded corner for controls -->
<dimen name="control_corner_material">2dp</dimen>
+
+ <!-- Default alpha value for disabled elements. -->
+ <item name="disabled_alpha_material" format="float" type="dimen">0.26</item>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4761f1a..dd1d433 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -154,6 +154,10 @@
<string name="ClipMmi">Incoming Caller ID</string>
<!-- Displayed as the title for a success/failure report enabling/disabling caller ID. -->
<string name="ClirMmi">Outgoing Caller ID</string>
+ <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. -->
+ <string name="ColpMmi">Connected Line ID</string>
+ <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. -->
+ <string name="ColrMmi">Connected Line ID Restriction</string>
<!-- Displayed as the title for a success/failure report enabling/disabling call forwarding. -->
<string name="CfMmi">Call forwarding</string>
<!-- Displayed as the title for a success/failure report enabling/disabling call waiting. -->
@@ -2540,15 +2544,19 @@
Contact your wireless service provider for another SIM card.</string>
<!-- Shown on transport control of lockscreen. Pressing button goes to previous track. -->
- <string name="lockscreen_transport_prev_description">Previous track button</string>
+ <string name="lockscreen_transport_prev_description">Previous track</string>
<!-- Shown on transport control of lockscreen. Pressing button goes to next track. -->
- <string name="lockscreen_transport_next_description">Next track button</string>
+ <string name="lockscreen_transport_next_description">Next track</string>
<!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
- <string name="lockscreen_transport_pause_description">Pause button</string>
+ <string name="lockscreen_transport_pause_description">Pause</string>
<!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
- <string name="lockscreen_transport_play_description">Play button</string>
+ <string name="lockscreen_transport_play_description">Play</string>
<!-- Shown on transport control of lockscreen. Pressing button pauses playback -->
- <string name="lockscreen_transport_stop_description">Stop button</string>
+ <string name="lockscreen_transport_stop_description">Stop</string>
+ <!-- Shown on transport control screens. Pressing button rewinds playback [CHAR LIMIT=NONE]-->
+ <string name="lockscreen_transport_rew_description">Rewind</string>
+ <!-- Shown on transport control screens. Pressing button fast forwards playback [CHAR LIMIT=NONE]-->
+ <string name="lockscreen_transport_ffw_description">Fast forward</string>
<!-- Shown in the lock screen when there is emergency calls only mode. -->
<string name="emergency_calls_only" msgid="2485604591272668370">Emergency calls only</string>
@@ -4134,6 +4142,10 @@
<string name="time_picker_increment_set_pm_button">Set PM</string>
<!-- Description of the button to decrease the TimePicker's set AM value. [CHAR LIMIT=NONE] -->
<string name="time_picker_decrement_set_am_button">Set AM</string>
+ <!-- Label for the TimePicker's PM button. [CHAR LIMIT=2] -->
+ <string name="time_picker_pm_label">PM</string>
+ <!-- Label for the TimePicker's AM button. [CHAR LIMIT=2] -->
+ <string name="time_picker_am_label">AM</string>
<!-- DatePicker - accessibility support -->
<!-- Description of the button to increase the DatePicker's month value. [CHAR LIMIT=NONE] -->
@@ -5061,5 +5073,5 @@
<!-- TV content rating system strings for ZA TV -->
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
- <string name="battery_saver_description">To help improve battery life, battery saver will reduce your device’s performance and restrict background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
+ <string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index a5cac6b..e693d91 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1105,26 +1105,32 @@
<style name="MediaButton.Previous">
<item name="src">@drawable/ic_media_previous</item>
+ <item name="contentDescription">@string/lockscreen_transport_prev_description</item>
</style>
<style name="MediaButton.Next">
<item name="src">@drawable/ic_media_next</item>
+ <item name="contentDescription">@string/lockscreen_transport_next_description</item>
</style>
<style name="MediaButton.Play">
<item name="src">@drawable/ic_media_play</item>
+ <item name="contentDescription">@string/lockscreen_transport_play_description</item>
</style>
<style name="MediaButton.Ffwd">
<item name="src">@drawable/ic_media_ff</item>
+ <item name="contentDescription">@string/lockscreen_transport_ffw_description</item>
</style>
<style name="MediaButton.Rew">
<item name="src">@drawable/ic_media_rew</item>
+ <item name="contentDescription">@string/lockscreen_transport_rew_description</item>
</style>
<style name="MediaButton.Pause">
<item name="src">@drawable/ic_media_pause</item>
+ <item name="contentDescription">@string/lockscreen_transport_pause_description</item>
</style>
<style name="ZoomControls">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 84bc62c..110ddb1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -382,6 +382,8 @@
<java-symbol type="string" name="CfMmi" />
<java-symbol type="string" name="ClipMmi" />
<java-symbol type="string" name="ClirMmi" />
+ <java-symbol type="string" name="ColpMmi" />
+ <java-symbol type="string" name="ColrMmi" />
<java-symbol type="string" name="CwMmi" />
<java-symbol type="string" name="Midnight" />
<java-symbol type="string" name="Noon" />
@@ -976,6 +978,8 @@
<java-symbol type="string" name="ssl_ca_cert_noti_by_unknown" />
<java-symbol type="string" name="ssl_ca_cert_noti_managed" />
<java-symbol type="string" name="ssl_ca_cert_warning" />
+ <java-symbol type="string" name="lockscreen_transport_play_description" />
+ <java-symbol type="string" name="lockscreen_transport_pause_description" />
<java-symbol type="plurals" name="abbrev_in_num_days" />
<java-symbol type="plurals" name="abbrev_in_num_hours" />
@@ -1969,9 +1973,7 @@
<java-symbol type="array" name="config_cdma_home_system" />
<java-symbol type="attr" name="headerSelectedTextColor" />
<java-symbol type="attr" name="amPmSelectedBackgroundColor" />
-
- <!--From SmsMessage-->
- <!--Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet
- string that's stored in 8-bit unpacked format) characters.-->
<java-symbol type="bool" name="config_sms_decode_gsm_8bit_data" />
+ <java-symbol type="string" name="time_picker_am_label" />
+ <java-symbol type="string" name="time_picker_pm_label" />
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index e7001c3..1376dfa 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -46,7 +46,7 @@
<item name="colorForegroundInverse">@color/bright_foreground_material_light</item>
<item name="colorBackground">@color/background_material_dark</item>
<item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
- <item name="disabledAlpha">0.5</item>
+ <item name="disabledAlpha">@dimen/disabled_alpha_material</item>
<item name="backgroundDimAmount">0.6</item>
<!-- Text styles -->
@@ -304,8 +304,8 @@
<item name="actionButtonStyle">@style/Widget.Material.ActionButton</item>
<item name="actionOverflowButtonStyle">@style/Widget.Material.ActionButton.Overflow</item>
<item name="actionOverflowMenuStyle">@style/Widget.Material.PopupMenu.Overflow</item>
- <item name="actionModeBackground">?attr/colorPrimaryDark</item>
- <item name="actionModeSplitBackground">?attr/colorPrimaryDark</item>
+ <item name="actionModeBackground">@drawable/cab_background_top_material</item>
+ <item name="actionModeSplitBackground">@drawable/cab_background_bottom_material</item>
<item name="actionModeCloseDrawable">@drawable/ic_cab_done_material</item>
<item name="actionBarTabStyle">@style/Widget.Material.ActionBar.TabView</item>
<item name="actionBarTabBarStyle">@style/Widget.Material.ActionBar.TabBar</item>
@@ -387,7 +387,7 @@
<item name="colorForegroundInverse">@color/bright_foreground_material_dark</item>
<item name="colorBackground">@color/background_material_light</item>
<item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
- <item name="disabledAlpha">0.5</item>
+ <item name="disabledAlpha">@dimen/disabled_alpha_material</item>
<item name="backgroundDimAmount">0.6</item>
<!-- Text styles -->
@@ -650,9 +650,8 @@
<item name="actionButtonStyle">@style/Widget.Material.Light.ActionButton</item>
<item name="actionOverflowButtonStyle">@style/Widget.Material.Light.ActionButton.Overflow</item>
<item name="actionOverflowMenuStyle">@style/Widget.Material.Light.PopupMenu.Overflow</item>
- <item name="actionBarPopupTheme">@null</item>
- <item name="actionModeBackground">@drawable/cab_background_top_holo_light</item>
- <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
+ <item name="actionModeBackground">@drawable/cab_background_top_material</item>
+ <item name="actionModeSplitBackground">@drawable/cab_background_bottom_material</item>
<item name="actionModeCloseDrawable">@drawable/ic_cab_done_material</item>
<item name="actionBarTabStyle">@style/Widget.Material.Light.ActionBar.TabView</item>
<item name="actionBarTabBarStyle">@style/Widget.Material.Light.ActionBar.TabBar</item>
@@ -663,6 +662,7 @@
<item name="actionBarSize">@dimen/action_bar_default_height_material</item>
<item name="actionModePopupWindowStyle">@style/Widget.Material.Light.PopupWindow.ActionMode</item>
<item name="actionBarWidgetTheme">@null</item>
+ <item name="actionBarPopupTheme">@null</item>
<item name="actionBarTheme">@style/ThemeOverlay.Material.ActionBar</item>
<item name="actionBarItemBackground">?attr/selectableItemBackgroundBorderless</item>
@@ -960,16 +960,8 @@
</style>
<!-- Theme for the search input bar. -->
-
- <style name="Theme.Material.SearchBar" parent="Theme.Material.Panel">
- <item name="actionModeBackground">@drawable/cab_background_top_holo_dark</item>
- <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
- </style>
-
- <style name="Theme.Material.Light.SearchBar" parent="Theme.Material.Light.Panel">
- <item name="actionModeBackground">@drawable/cab_background_top_holo_light</item>
- <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
- </style>
+ <style name="Theme.Material.SearchBar" parent="Theme.Material.Panel" />
+ <style name="Theme.Material.Light.SearchBar" parent="Theme.Material.Light.Panel" />
<!-- Menu Themes -->
<eat-comment />
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
new file mode 100644
index 0000000..a4d4906
--- /dev/null
+++ b/data/fonts/fonts.xml
@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="utf-8"?>
+<familyset version="22">
+ <!-- first font is default -->
+ <family name="sans-serif">
+ <font weight="100" style="normal">Roboto-Thin.ttf</font>
+ <font weight="100" style="italic">Roboto-ThinItalic.ttf</font>
+ <font weight="300" style="normal">Roboto-Light.ttf</font>
+ <font weight="300" style="italic">Roboto-LightItalic.ttf</font>
+ <font weight="400" style="normal">Roboto-Regular.ttf</font>
+ <font weight="400" style="italic">Roboto-Italic.ttf</font>
+ <font weight="500" style="normal">Roboto-Medium.ttf</font>
+ <font weight="500" style="italic">Roboto-MediumItalic.ttf</font>
+ <font weight="700" style="normal">Roboto-Bold.ttf</font>
+ <font weight="700" style="italic">Roboto-BoldItalic.ttf</font>
+ <font weight="900" style="normal">Roboto-Black.ttf</font>
+ <font weight="900" style="italic">Roboto-BlackItalic.ttf</font>
+ </family>
+
+ <!-- Note that aliases must come after the fonts they reference. -->
+ <alias name="sans-serif-thin" to="sans-serif" weight="100" />
+ <alias name="sans-serif-light" to="sans-serif" weight="300" />
+ <alias name="sans-serif-black" to="sans-serif" weight="900" />
+ <alias name="arial" to="sans-serif" />
+ <alias name="helvetica" to="sans-serif" />
+ <alias name="tahoma" to="sans-serif" />
+ <alias name="verdana" to="sans-serif" />
+
+ <family name="sans-serif-condensed">
+ <font weight="300" style="normal">RobotoCondensed-Light.ttf</font>
+ <font weight="300" style="italic">RobotoCondensed-LightItalic.ttf</font>
+ <font weight="400" style="normal">RobotoCondensed-Regular.ttf</font>
+ <font weight="400" style="italic">RobotoCondensed-Italic.ttf</font>
+ <font weight="700" style="normal">RobotoCondensed-Bold.ttf</font>
+ <font weight="700" style="italic">RobotoCondensed-BoldItalic.ttf</font>
+ </family>
+ <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" />
+
+ <family name="serif">
+ <font weight="400" style="normal">NotoSerif-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSerif-Bold.ttf</font>
+ <font weight="400" style="italic">NotoSerif-Italic.ttf</font>
+ <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font>
+ </family>
+ <alias name="times" to="serif" />
+ <alias name="times new roman" to="serif" />
+ <alias name="palatino" to="serif" />
+ <alias name="georgia" to="serif" />
+ <alias name="baskerville" to="serif" />
+ <alias name="goudy" to="serif" />
+ <alias name="fantasy" to="serif" />
+ <alias name="ITC Stone Serif" to="serif" />
+
+ <family name="monospace">
+ <font weight="400" style="normal">DroidSansMono.ttf</font>
+ </family>
+ <alias name="courier" to="monospace" />
+ <alias name="courier new" to="monospace" />
+ <alias name="monaco" to="monospace" />
+
+ <family name="casual">
+ <font weight="400" style="normal">ComingSoon.ttf</font>
+ </family>
+
+ <family name="cursive">
+ <font weight="400" style="normal">DancingScript-Regular.ttf</font>
+ <font weight="700" style="normal">DancingScript-Bold.ttf</font>
+ </family>
+
+ <family name="sans-serif-smallcaps">
+ <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
+ </family>
+
+ <!-- fallback fonts -->
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoNaskh-Regular.ttf</font>
+ <font weight="700" style="normal">NotoNaskh-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoNaskhUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoNaskhUI-Bold.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
+ </family>
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansThai-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansArmenian-Bold.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansGeorgian-Bold.ttf</font>
+ </family>
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansDevanagari-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansDevanagari-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansDevanagariUI-Bold.ttf</font>
+ </family>
+ <!-- Gujarati should come after Devanagari -->
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
+ </family>
+ <!-- Gurmukhi should come after Devanagari -->
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansGurmukhi-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansGurmukhi-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansGurmukhiUI-Bold.ttf</font>
+ </family>
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansTamil-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansTamil-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansTamilUI-Bold.ttf</font>
+ </family>
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansMalayalam-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansMalayalam-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansMalayalamUI-Bold.ttf</font>
+ </family>
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansBengali-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansBengali-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansBengaliUI-Bold.ttf</font>
+ </family>
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansTelugu-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansTelugu-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansTeluguUI-Bold.ttf</font>
+ </family>
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansKannada-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansKannada-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansKannadaUI-Bold.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansSinhala-Bold.ttf</font>
+ </family>
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansKhmer-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansKhmer-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
+ </family>
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansLao-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
+ </family>
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansMyanmar-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansMyanmar-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansMyanmarUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansMyanmarUI-Bold.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
+ </family>
+ <family lang="zh-Hans">
+ <font weight="400" style="normal">NotoSansHans-Regular.otf</font>
+ </family>
+ <family lang="zh-Hant">
+ <font weight="400" style="normal">NotoSansHant-Regular.otf</font>
+ </family>
+ <family lang="ja">
+ <font weight="400" style="normal">NotoSansJP-Regular.otf</font>
+ </family>
+ <family lang="ko">
+ <font weight="400" style="normal">NotoSansKR-Regular.otf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NanumGothic.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoColorEmoji.ttf</font>
+ </family>
+ <family lang="ja">
+ <font weight="400" style="normal">MTLmr3m.ttf</font>
+ </family>
+</familyset>
diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd
index 864e82e..851827c 100644
--- a/docs/html/sdk/installing/installing-adt.jd
+++ b/docs/html/sdk/installing/installing-adt.jd
@@ -1,8 +1,8 @@
page.title=Installing the Eclipse Plugin
-adt.zip.version=23.0.2
-adt.zip.download=ADT-23.0.2.zip
-adt.zip.bytes=103287135
-adt.zip.checksum=cde1d0a463b5ccce844b63161cfa1cb9
+adt.zip.version=23.0.3
+adt.zip.download=ADT-23.0.3.zip
+adt.zip.bytes=103321934
+adt.zip.checksum=ab2f5e2fbbdddeeb7dfd02cd4046538a
@jd:body
diff --git a/docs/html/tools/sdk/eclipse-adt.jd b/docs/html/tools/sdk/eclipse-adt.jd
index 5d04098..cf33200 100644
--- a/docs/html/tools/sdk/eclipse-adt.jd
+++ b/docs/html/tools/sdk/eclipse-adt.jd
@@ -56,6 +56,42 @@
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+ alt=""/>ADT 23.0.3</a> <em>(August 2014)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+<dl>
+ <dt>Dependencies:</dt>
+
+ <dd>
+ <ul>
+ <li>Java 7 or higher is required if you are targeting the L Developer Preview.</li>
+ <li>Java 1.6 or higher is required if you are targeting other releases.</li>
+ <li>Eclipse Indigo (Version 3.7.2) or higher is required.</li>
+ <li>This version of ADT is designed for use with
+ <a href="{@docRoot}tools/sdk/tools-notes.html">SDK Tools r23.0.2</a>.
+ If you haven't already installed SDK Tools r23.0.2 into your SDK, use the
+ Android SDK Manager to do so.</li>
+ </ul>
+ </dd>
+
+ <dt>General Notes:</dt>
+ <dd>
+ <ul>
+ <li>Fixed an issue where ADT displayed a <code>NullPointerException</code> warning dialog
+ when a valid SDK was not configured. (<a href="http://b.android.com/73313">Issue
+ 73313</a>)</li>
+ <li>Fixed a minor issue with RenderScript support.</li>
+ <li>Disabled APK compression.</li>
+ </ul>
+ </dd>
+</dl>
+</div>
+</div>
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
alt=""/>ADT 23.0.2</a> <em>(July 2014)</em>
</p>
diff --git a/docs/html/tools/support-library/features.jd b/docs/html/tools/support-library/features.jd
index 65148bf..78946ee 100644
--- a/docs/html/tools/support-library/features.jd
+++ b/docs/html/tools/support-library/features.jd
@@ -225,8 +225,7 @@
<p>This library provides {@link android.support.v7.media.MediaRouter}, {@link
android.support.v7.media.MediaRouteProvider}, and related media classes that
-support the <a href="https://developers.google.com/cast/">Google Cast
-developer preview</a>. </p>
+support <a href="https://developers.google.com/cast/docs/android_sender">Google Cast</a>. </p>
<p>In general, the APIs in the v7 mediarouter library provide a means of
controlling the routing of media channels and streams from the current device to
@@ -258,9 +257,8 @@
<p class="caution">The v7 mediarouter library APIs introduced in Support Library
r18 are subject to change in later revisions of the Support Library. At this
-time, we recommend using the library only in connection with the <a
-href="https://developers.google.com/cast/">Google Cast
-developer preview</a>. </p>
+time, we recommend using the library only in connection with <a
+href="https://developers.google.com/cast/docs/android_sender">Google Cast</a>. </p>
<h2 id="v8">v8 Support Library</h2>
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 6ebb8b5..a6dbcb0 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -626,13 +626,18 @@
mAutoMirrored = autoMirror;
// Sanity check for valid padding when we have optical insets.
- if (mPadding.left < mOpticalInsets.left) {
- mPadding.left = mOpticalInsets.left;
- mPadding.right = mOpticalInsets.right;
- }
- if (mPadding.top < mOpticalInsets.top) {
- mPadding.top = mOpticalInsets.top;
- mPadding.bottom = mOpticalInsets.bottom;
+ if (!opticalInsets.isEmpty()) {
+ if (mPadding == null) {
+ mPadding = new Rect();
+ }
+ if (mPadding.left < opticalInsets.left) {
+ mPadding.left = opticalInsets.left;
+ mPadding.right = opticalInsets.right;
+ }
+ if (mPadding.top < opticalInsets.top) {
+ mPadding.top = opticalInsets.top;
+ mPadding.bottom = opticalInsets.bottom;
+ }
}
}
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 6f21f2e..be2241b 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -276,7 +276,7 @@
public void getBounds(Rect bounds) {
final int outerX = (int) mOuterX;
final int outerY = (int) mOuterY;
- final int r = (int) mOuterRadius;
+ final int r = (int) mOuterRadius + 1;
bounds.set(outerX - r, outerY - r, outerX + r, outerY + r);
}
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index d404ccd..93df648 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -264,7 +264,7 @@
public void getBounds(Rect bounds) {
final int outerX = (int) mOuterX;
final int outerY = (int) mOuterY;
- final int r = (int) mOuterRadius;
+ final int r = (int) mOuterRadius + 1;
bounds.set(outerX - r, outerY - r, outerX + r, outerY + r);
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 813797f..2b5823e 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -104,6 +104,8 @@
* <dt><code>android:translateY</code></dt>
* <dd>The amount of translation on the Y coordinate.
* This is defined in the viewport space.</dd>
+ * <dt><code>android:alpha</code></dt>
+ * <dd>The amount of transparency.</dd>
* </dl></dd>
* </dl>
*
@@ -117,15 +119,11 @@
* <dd>Defines path string. This is using exactly same format as "d" attribute
* in the SVG's path data. This is defined in the viewport space.</dd>
* <dt><code>android:fillColor</code></dt>
- * <dd>Defines the color to fill the path (none if not present).</dd>
+ * <dd>Defines the color to fill the path (black if not present).</dd>
* <dt><code>android:strokeColor</code></dt>
* <dd>Defines the color to draw the path outline (none if not present).</dd>
* <dt><code>android:strokeWidth</code></dt>
* <dd>The width a path stroke.</dd>
- * <dt><code>android:strokeOpacity</code></dt>
- * <dd>The opacity of a path stroke.</dd>
- * <dt><code>android:fillOpacity</code></dt>
- * <dd>The opacity to fill the path with.</dd>
* <dt><code>android:trimPathStart</code></dt>
* <dd>The fraction of the path to trim from the start, in the range from 0 to 1.</dd>
* <dt><code>android:trimPathEnd</code></dt>
@@ -1241,10 +1239,8 @@
int mStrokeColor = 0;
float mStrokeWidth = 0;
- float mStrokeOpacity = Float.NaN;
int mFillColor = Color.BLACK;
int mFillRule;
- float mFillOpacity = Float.NaN;
float mTrimPathStart = 0;
float mTrimPathEnd = 1;
float mTrimPathOffset = 0;
@@ -1263,10 +1259,8 @@
mStrokeColor = copy.mStrokeColor;
mStrokeWidth = copy.mStrokeWidth;
- mStrokeOpacity = copy.mStrokeOpacity;
mFillColor = copy.mFillColor;
mFillRule = copy.mFillRule;
- mFillOpacity = copy.mFillOpacity;
mTrimPathStart = copy.mTrimPathStart;
mTrimPathEnd = copy.mTrimPathEnd;
mTrimPathOffset = copy.mTrimPathOffset;
@@ -1327,8 +1321,6 @@
mFillColor = a.getColor(R.styleable.VectorDrawablePath_fillColor,
mFillColor);
- mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity,
- mFillOpacity);
mStrokeLineCap = getStrokeLineCap(a.getInt(
R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap);
mStrokeLineJoin = getStrokeLineJoin(a.getInt(
@@ -1337,8 +1329,6 @@
R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit);
mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_strokeColor,
mStrokeColor);
- mStrokeOpacity = a.getFloat(R.styleable.VectorDrawablePath_strokeOpacity,
- mStrokeOpacity);
mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth,
mStrokeWidth);
mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd,
@@ -1347,8 +1337,6 @@
R.styleable.VectorDrawablePath_trimPathOffset, mTrimPathOffset);
mTrimPathStart = a.getFloat(
R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart);
-
- updateColorAlphas();
}
@Override
@@ -1363,16 +1351,6 @@
a.recycle();
}
- private void updateColorAlphas() {
- if (!Float.isNaN(mFillOpacity)) {
- mFillColor = applyAlpha(mFillColor, mFillOpacity);
- }
-
- if (!Float.isNaN(mStrokeOpacity)) {
- mStrokeColor = applyAlpha(mStrokeColor, mStrokeOpacity);
- }
- }
-
/* Setters and Getters, used by animator from AnimatedVectorDrawable. */
@SuppressWarnings("unused")
int getStroke() {
@@ -1395,16 +1373,6 @@
}
@SuppressWarnings("unused")
- float getStrokeOpacity() {
- return mStrokeOpacity;
- }
-
- @SuppressWarnings("unused")
- void setStrokeOpacity(float strokeOpacity) {
- mStrokeOpacity = strokeOpacity;
- }
-
- @SuppressWarnings("unused")
int getFill() {
return mFillColor;
}
@@ -1415,16 +1383,6 @@
}
@SuppressWarnings("unused")
- float getFillOpacity() {
- return mFillOpacity;
- }
-
- @SuppressWarnings("unused")
- void setFillOpacity(float fillOpacity) {
- mFillOpacity = fillOpacity;
- }
-
- @SuppressWarnings("unused")
float getTrimPathStart() {
return mTrimPathStart;
}
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 79a2f61..acfa98e 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -69,7 +69,9 @@
class PlaybackStateStruct {
protected:
PlaybackStateStruct(OpenGLRenderer& renderer, int replayFlags, LinearAllocator* allocator)
- : mRenderer(renderer), mReplayFlags(replayFlags), mAllocator(allocator){}
+ : mRenderer(renderer)
+ , mReplayFlags(replayFlags)
+ , mAllocator(allocator) {}
public:
OpenGLRenderer& mRenderer;
@@ -78,6 +80,15 @@
// Allocator with the lifetime of a single frame.
// replay uses an Allocator owned by the struct, while defer shares the DeferredDisplayList's Allocator
LinearAllocator * const mAllocator;
+
+ SkPath* allocPathForFrame() {
+ mTempPaths.push_back();
+ return &mTempPaths.back();
+ }
+
+private:
+ // Paths kept alive for the duration of the frame
+ std::vector<SkPath> mTempPaths;
};
class DeferStateStruct : public PlaybackStateStruct {
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 6883cc5..c6d3db7 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1505,20 +1505,18 @@
class DrawShadowOp : public DrawOp {
public:
DrawShadowOp(const mat4& transformXY, const mat4& transformZ,
- float casterAlpha, const SkPath* casterOutline, const SkPath* revealClip)
- : DrawOp(NULL), mTransformXY(transformXY), mTransformZ(transformZ),
- mCasterAlpha(casterAlpha) {
- mOutline = *casterOutline;
- if (revealClip) {
- // intersect the outline with the convex reveal clip
- Op(mOutline, *revealClip, kIntersect_PathOp, &mOutline);
- }
+ float casterAlpha, const SkPath* casterOutline)
+ : DrawOp(NULL)
+ , mTransformXY(transformXY)
+ , mTransformZ(transformZ)
+ , mCasterAlpha(casterAlpha)
+ , mCasterOutline(casterOutline) {
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
const DeferredDisplayState& state) {
renderer.getCaches().tessellationCache.precacheShadows(&state.mMatrix,
- renderer.getLocalClipBounds(), isCasterOpaque(), &mOutline,
+ renderer.getLocalClipBounds(), isCasterOpaque(), mCasterOutline,
&mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius());
}
@@ -1527,7 +1525,7 @@
Matrix4 drawTransform;
renderer.getMatrix(&drawTransform);
renderer.getCaches().tessellationCache.getShadowBuffers(&drawTransform,
- renderer.getLocalClipBounds(), isCasterOpaque(), &mOutline,
+ renderer.getLocalClipBounds(), isCasterOpaque(), mCasterOutline,
&mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius(),
buffers);
@@ -1546,7 +1544,7 @@
const mat4 mTransformXY;
const mat4 mTransformZ;
const float mCasterAlpha;
- SkPath mOutline;
+ const SkPath* mCasterOutline;
};
class DrawLayerOp : public DrawOp {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e00d2e3..396c7f3 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -58,11 +58,6 @@
// Defines
///////////////////////////////////////////////////////////////////////////////
-#define RAD_TO_DEG (180.0f / 3.14159265f)
-#define MIN_ANGLE 0.001f
-
-#define ALPHA_THRESHOLD 0
-
static GLenum getFilter(const SkPaint* paint) {
if (!paint || paint->getFilterLevel() != SkPaint::kNone_FilterLevel) {
return GL_LINEAR;
@@ -692,7 +687,7 @@
(fboLayer && clip.isEmpty())) {
mSnapshot->empty = fboLayer;
} else {
- mSnapshot->invisible = mSnapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
+ mSnapshot->invisible = mSnapshot->invisible || (alpha <= 0 && fboLayer);
}
}
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 209341c..e30ac19 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -49,6 +49,7 @@
#include "Matrix.h"
#include "Vector.h"
#include "Vertex.h"
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
@@ -56,12 +57,11 @@
#define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f)
#define ROUND_CAP_THRESH 0.25f
#define PI 3.1415926535897932f
+#define MAX_DEPTH 15
-// temporary error thresholds
-#define ERROR_DEPTH 20
-#define ERROR_SCALE 1e10
-#define ERROR_SQR_INV_THRESH 1e-20
-
+/**
+ * Extracts the x and y scale from the transform as positive values, and clamps them
+ */
void PathTessellator::extractTessellationScales(const Matrix4& transform,
float* scaleX, float* scaleY) {
if (CC_LIKELY(transform.isPureTranslate())) {
@@ -72,11 +72,8 @@
float m01 = transform.data[Matrix4::kSkewY];
float m10 = transform.data[Matrix4::kSkewX];
float m11 = transform.data[Matrix4::kScaleY];
- *scaleX = sqrt(m00 * m00 + m01 * m01);
- *scaleY = sqrt(m10 * m10 + m11 * m11);
-
- LOG_ALWAYS_FATAL_IF(*scaleX > ERROR_SCALE || *scaleY > ERROR_SCALE,
- "scales %e x %e too large for tessellation", *scaleX, *scaleY);
+ *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01));
+ *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11));
}
}
@@ -109,8 +106,8 @@
} else {
float scaleX, scaleY;
PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
- inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
- inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
+ inverseScaleX = 1.0f / scaleX;
+ inverseScaleY = 1.0f / scaleY;
}
if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
@@ -914,9 +911,6 @@
Vector<Vertex>& outputVertices) {
ATRACE_CALL();
- LOG_ALWAYS_FATAL_IF(sqrInvScaleX < ERROR_SQR_INV_THRESH || sqrInvScaleY < ERROR_SQR_INV_THRESH,
- "Invalid scale factors used for approx %e, %e", sqrInvScaleX, sqrInvScaleY);
-
// TODO: to support joins other than sharp miter, join vertices should be labelled in the
// perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
SkPath::Iter iter(path, forceClose);
@@ -975,9 +969,6 @@
float p2x, float p2y, float c2x, float c2y,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Vector<Vertex>& outputVertices, int depth) {
- LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR DEPTH exceeded: cubic approx, invscale %e x %e, vertcount %d",
- sqrInvScaleX, sqrInvScaleY, outputVertices.size());
-
float dx = p2x - p1x;
float dy = p2y - p1y;
float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
@@ -985,7 +976,8 @@
float d = d1 + d2;
// multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
- if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ if (depth >= MAX_DEPTH
+ || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, p2x, p2y);
} else {
@@ -1023,14 +1015,13 @@
float cx, float cy,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Vector<Vertex>& outputVertices, int depth) {
- LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR_DEPTH exceeded: quadratic approx, invscale %e x %e, vertcount %d",
- sqrInvScaleX, sqrInvScaleY, outputVertices.size());
-
float dx = bx - ax;
float dy = by - ay;
float d = (cx - bx) * dy - (cy - by) * dx;
- if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
+ if (depth >= MAX_DEPTH
+ || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, bx, by);
} else {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index f48b774..23940ee 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -534,6 +534,7 @@
inline void endMark() {}
inline int level() { return mLevel; }
inline int replayFlags() { return mDeferStruct.mReplayFlags; }
+ inline SkPath* allocPathForFrame() { return mDeferStruct.allocPathForFrame(); }
private:
DeferStateStruct& mDeferStruct;
@@ -564,6 +565,7 @@
}
inline int level() { return mLevel; }
inline int replayFlags() { return mReplayStruct.mReplayFlags; }
+ inline SkPath* allocPathForFrame() { return mReplayStruct.allocPathForFrame(); }
private:
ReplayStateStruct& mReplayStruct;
@@ -612,14 +614,24 @@
mat4 shadowMatrixZ(transformFromParent);
applyViewPropertyTransforms(shadowMatrixZ, true);
- const SkPath* outlinePath = properties().getOutline().getPath();
+ const SkPath* casterOutlinePath = properties().getOutline().getPath();
const SkPath* revealClipPath = properties().getRevealClip().getPath();
if (revealClipPath && revealClipPath->isEmpty()) return;
float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha();
+
+ const SkPath* outlinePath = casterOutlinePath;
+ if (revealClipPath) {
+ // if we can't simply use the caster's path directly, create a temporary one
+ SkPath* frameAllocatedPath = handler.allocPathForFrame();
+
+ // intersect the outline with the convex reveal clip
+ Op(*casterOutlinePath, *revealClipPath, kIntersect_PathOp, frameAllocatedPath);
+ outlinePath = frameAllocatedPath;
+ }
+
DisplayListOp* shadowOp = new (handler.allocator()) DrawShadowOp(
- shadowMatrixXY, shadowMatrixZ, casterAlpha,
- outlinePath, revealClipPath);
+ shadowMatrixXY, shadowMatrixZ, casterAlpha, outlinePath);
handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 41f48cd..0c8d07f 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -313,7 +313,6 @@
}
bool setScaleX(float scaleX) {
- LOG_ALWAYS_FATAL_IF(scaleX > 1000000, "invalid scaleX %e", scaleX);
return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX);
}
@@ -322,7 +321,6 @@
}
bool setScaleY(float scaleY) {
- LOG_ALWAYS_FATAL_IF(scaleY > 1000000, "invalid scaleY %e", scaleY);
return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY);
}
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 66bc127..6fb0411 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -35,6 +35,9 @@
return value >= NON_ZERO_EPSILON;
}
+ /**
+ * Clamps alpha value, and snaps when very near 0 or 1
+ */
inline static float clampAlpha(float alpha) {
if (alpha <= ALPHA_EPSILON) {
return 0;
@@ -45,6 +48,20 @@
}
}
+ /*
+ * Clamps positive tessellation scale values
+ */
+ inline static float clampTessellationScale(float scale) {
+ const float MIN_SCALE = 0.0001;
+ const float MAX_SCALE = 1e10;
+ if (scale < MIN_SCALE) {
+ return MIN_SCALE;
+ } else if (scale > MAX_SCALE) {
+ return MAX_SCALE;
+ }
+ return scale;
+ }
+
inline static bool areEqual(float valueA, float valueB) {
return isZero(valueA - valueB);
}
diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl
index dd84ad32..3d25aa6 100644
--- a/media/java/android/media/projection/IMediaProjection.aidl
+++ b/media/java/android/media/projection/IMediaProjection.aidl
@@ -25,7 +25,7 @@
boolean canProjectAudio();
boolean canProjectVideo();
boolean canProjectSecureVideo();
- int getVirtualDisplayFlags();
+ int applyVirtualDisplayFlags(int flags);
void addCallback(IMediaProjectionCallback callback);
void removeCallback(IMediaProjectionCallback callback);
}
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 7c03171..861039d 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -93,6 +93,19 @@
}
/**
+ * @hide
+ */
+ public VirtualDisplay createVirtualDisplay(@NonNull String name,
+ int width, int height, int dpi, boolean isSecure, @Nullable Surface surface,
+ @Nullable VirtualDisplay.Callbacks callbacks, @Nullable Handler handler) {
+ DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ int flags = isSecure ? DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE : 0;
+ return dm.createVirtualDisplay(this, name, width, height, dpi, surface,
+ flags | DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR |
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION, callbacks, handler);
+ }
+
+ /**
* Creates a {@link android.hardware.display.VirtualDisplay} to capture the
* contents of the screen.
*
@@ -105,9 +118,8 @@
* than 0.
* @param surface The surface to which the content of the virtual display
* should be rendered, or null if there is none initially.
- * @param isSecure Whether the display should be considered a secure
- * display. This typically requires special permissions not available to
- * third party applications.
+ * @param flags A combination of virtual display flags. See {@link DisplayManager} for the full
+ * list of flags.
* @param callbacks Callbacks to call when the virtual display's state
* changes, or null if none.
* @param handler The {@link android.os.Handler} on which the callback should be
@@ -118,10 +130,9 @@
* String, int, int, int, int, Surface, VirtualDisplay.Callbacks, Handler)
*/
public VirtualDisplay createVirtualDisplay(@NonNull String name,
- int width, int height, int dpi, boolean isSecure, @Nullable Surface surface,
+ int width, int height, int dpi, int flags, @Nullable Surface surface,
@Nullable VirtualDisplay.Callbacks callbacks, @Nullable Handler handler) {
DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
- int flags = isSecure ? DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE : 0;
return dm.createVirtualDisplay(
this, name, width, height, dpi, surface, flags, callbacks, handler);
}
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
index c98a48d..7a853d1 100644
--- a/media/java/android/media/tv/ITvInputService.aidl
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -35,6 +35,6 @@
// For hardware TvInputService
void notifyHardwareAdded(in TvInputHardwareInfo hardwareInfo);
void notifyHardwareRemoved(in TvInputHardwareInfo hardwareInfo);
- void notifyHdmiCecDeviceAdded(in HdmiDeviceInfo deviceInfo);
- void notifyHdmiCecDeviceRemoved(in HdmiDeviceInfo deviceInfo);
+ void notifyHdmiDeviceAdded(in HdmiDeviceInfo deviceInfo);
+ void notifyHdmiDeviceRemoved(in HdmiDeviceInfo deviceInfo);
}
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
index df648e7..de5d56f 100644
--- a/media/java/android/media/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -24,7 +24,7 @@
* @hide
*/
oneway interface ITvInputServiceCallback {
- void addHardwareTvInput(in int deviceID, in TvInputInfo inputInfo);
- void addHdmiCecTvInput(in int logicalAddress, in TvInputInfo inputInfo);
+ void addHardwareTvInput(in int deviceId, in TvInputInfo inputInfo);
+ void addHdmiTvInput(in int logicalAddress, in TvInputInfo inputInfo);
void removeTvInput(in String inputId);
}
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 2e4d031..18136e9 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -19,7 +19,6 @@
import android.annotation.SystemApi;
import android.net.Uri;
import android.text.TextUtils;
-import android.util.Log;
import java.util.Arrays;
import java.util.Collections;
@@ -963,8 +962,6 @@
* </table>
*/
public final class TvContentRating {
- private static final String TAG = "TvContentRating";
-
/** @hide */
public static final Uri SYSTEM_CONTENT_RATING_SYSTEM_XML = Uri.parse(
"android.resource://system/" + com.android.internal.R.xml.tv_content_rating_systems);
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index a826957..ae6f5bc 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -20,7 +20,6 @@
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentUris;
-import android.media.tv.TvContract.Programs;
import android.net.Uri;
import android.provider.BaseColumns;
import android.util.ArraySet;
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 8feb7e6..8d0e986 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -103,10 +103,10 @@
private static final String XML_START_TAG_NAME = "tv-input";
private static final String DELIMITER_INFO_IN_ID = "/";
- private static final String PREFIX_CEC_DEVICE = "CEC";
+ private static final String PREFIX_HDMI_DEVICE = "HDMI";
private static final String PREFIX_HARDWARE_DEVICE = "HW";
- private static final int LENGTH_CEC_PHYSICAL_ADDRESS = 4;
- private static final int LENGTH_CEC_LOGICAL_ADDRESS = 2;
+ private static final int LENGTH_HDMI_PHYSICAL_ADDRESS = 4;
+ private static final int LENGTH_HDMI_LOGICAL_ADDRESS = 2;
private final ResolveInfo mService;
private final String mId;
@@ -155,7 +155,7 @@
* instantiating it from the given Context, ResolveInfo, and HdmiDeviceInfo.
*
* @param service The ResolveInfo returned from the package manager about this TV input service.
- * @param cecInfo The HdmiDeviceInfo for a HDMI CEC logical device.
+ * @param deviceInfo The HdmiDeviceInfo for a HDMI CEC logical device.
* @param parentId The ID of this TV input's parent input. {@code null} if none exists.
* @param iconUri The {@link android.net.Uri} to load the icon image.
* {@see android.content.ContentResolver#openInputStream}. If it is null, the application
@@ -166,12 +166,12 @@
*/
@SystemApi
public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
- HdmiDeviceInfo cecInfo, String parentId, String label, Uri iconUri)
+ HdmiDeviceInfo deviceInfo, String parentId, String label, Uri iconUri)
throws XmlPullParserException, IOException {
- boolean isConnectedToHdmiSwitch = (cecInfo.getPhysicalAddress() & 0x0FFF) != 0;
- return createTvInputInfo(context, service, generateInputIdForHdmiCec(
+ boolean isConnectedToHdmiSwitch = (deviceInfo.getPhysicalAddress() & 0x0FFF) != 0;
+ return createTvInputInfo(context, service, generateInputIdForHdmiDevice(
new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
- cecInfo), parentId, TYPE_HDMI, label, iconUri, isConnectedToHdmiSwitch);
+ deviceInfo), parentId, TYPE_HDMI, label, iconUri, isConnectedToHdmiSwitch);
}
/**
@@ -497,16 +497,16 @@
* Used to generate an input id from a ComponentName and HdmiDeviceInfo.
*
* @param name the component name for generating an input id.
- * @param cecInfo HdmiDeviceInfo describing this TV input.
- * @return the generated input id for the given {@code name} and {@code cecInfo}.
+ * @param deviceInfo HdmiDeviceInfo describing this TV input.
+ * @return the generated input id for the given {@code name} and {@code deviceInfo}.
*/
- private static final String generateInputIdForHdmiCec(
- ComponentName name, HdmiDeviceInfo cecInfo) {
- // Example of the format : "/CEC%04X%02X"
- String format = String.format("%s%s%%0%sX%%0%sX", DELIMITER_INFO_IN_ID, PREFIX_CEC_DEVICE,
- LENGTH_CEC_PHYSICAL_ADDRESS, LENGTH_CEC_LOGICAL_ADDRESS);
+ private static final String generateInputIdForHdmiDevice(
+ ComponentName name, HdmiDeviceInfo deviceInfo) {
+ // Example of the format : "/HDMI%04X%02X"
+ String format = String.format("%s%s%%0%sX%%0%sX", DELIMITER_INFO_IN_ID, PREFIX_HDMI_DEVICE,
+ LENGTH_HDMI_PHYSICAL_ADDRESS, LENGTH_HDMI_LOGICAL_ADDRESS);
return name.flattenToShortString() + String.format(format,
- cecInfo.getPhysicalAddress(), cecInfo.getLogicalAddress());
+ deviceInfo.getPhysicalAddress(), deviceInfo.getLogicalAddress());
}
/**
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 6a41c61..408ee7b 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -28,7 +28,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -42,7 +41,6 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
-import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.CaptioningManager;
@@ -92,7 +90,7 @@
* Handler instance to handle request from TV Input Manager Service. Should be run in the main
* looper to be synchronously run with {@code Session.mHandler}.
*/
- private Handler mServiceHandler = new ServiceHandler();
+ private final Handler mServiceHandler = new ServiceHandler();
private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
new RemoteCallbackList<ITvInputServiceCallback>();
@@ -142,15 +140,15 @@
}
@Override
- public void notifyHdmiCecDeviceAdded(HdmiDeviceInfo cecDeviceInfo) {
- mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HDMI_CEC_TV_INPUT,
- cecDeviceInfo).sendToTarget();
+ public void notifyHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
+ mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HDMI_TV_INPUT,
+ deviceInfo).sendToTarget();
}
@Override
- public void notifyHdmiCecDeviceRemoved(HdmiDeviceInfo cecDeviceInfo) {
- mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HDMI_CEC_TV_INPUT,
- cecDeviceInfo).sendToTarget();
+ public void notifyHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
+ mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HDMI_TV_INPUT,
+ deviceInfo).sendToTarget();
}
};
}
@@ -203,27 +201,27 @@
/**
* Returns a new {@link TvInputInfo} object if this service is responsible for
- * {@code cecDeviceInfo}; otherwise, return {@code null}. Override to modify default behavior
- * of ignoring all HDMI CEC logical input device.
+ * {@code deviceInfo}; otherwise, return {@code null}. Override to modify default behavior of
+ * ignoring all HDMI logical input device.
*
- * @param cecDeviceInfo {@link HdmiDeviceInfo} object just added.
+ * @param deviceInfo {@link HdmiDeviceInfo} object just added.
* @hide
*/
@SystemApi
- public TvInputInfo onHdmiCecDeviceAdded(HdmiDeviceInfo cecDeviceInfo) {
+ public TvInputInfo onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
return null;
}
/**
- * Returns the input ID for {@code logicalAddress} if it is handled by this service;
- * otherwise, return {@code null}. Override to modify default behavior of ignoring all HDMI CEC
- * logical input device.
+ * Returns the input ID for {@code logicalAddress} if it is handled by this service; otherwise,
+ * return {@code null}. Override to modify default behavior of ignoring all HDMI logical input
+ * device.
*
- * @param cecDeviceInfo {@link HdmiDeviceInfo} object just removed.
+ * @param deviceInfo {@link HdmiDeviceInfo} object just removed.
* @hide
*/
@SystemApi
- public String onHdmiCecDeviceRemoved(HdmiDeviceInfo cecDeviceInfo) {
+ public String onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
return null;
}
@@ -1164,8 +1162,8 @@
private static final int DO_NOTIFY_SESSION_CREATED = 2;
private static final int DO_ADD_HARDWARE_TV_INPUT = 3;
private static final int DO_REMOVE_HARDWARE_TV_INPUT = 4;
- private static final int DO_ADD_HDMI_CEC_TV_INPUT = 5;
- private static final int DO_REMOVE_HDMI_CEC_TV_INPUT = 6;
+ private static final int DO_ADD_HDMI_TV_INPUT = 5;
+ private static final int DO_REMOVE_HDMI_TV_INPUT = 6;
private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
int n = mCallbacks.beginBroadcast();
@@ -1179,12 +1177,11 @@
mCallbacks.finishBroadcast();
}
- private void broadcastAddHdmiCecTvInput(
- int logicalAddress, TvInputInfo inputInfo) {
+ private void broadcastAddHdmiTvInput(int logicalAddress, TvInputInfo inputInfo) {
int n = mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
- mCallbacks.getBroadcastItem(i).addHdmiCecTvInput(logicalAddress, inputInfo);
+ mCallbacks.getBroadcastItem(i).addHdmiTvInput(logicalAddress, inputInfo);
} catch (RemoteException e) {
Log.e(TAG, "Error while broadcasting.", e);
}
@@ -1286,17 +1283,17 @@
}
return;
}
- case DO_ADD_HDMI_CEC_TV_INPUT: {
- HdmiDeviceInfo cecDeviceInfo = (HdmiDeviceInfo) msg.obj;
- TvInputInfo inputInfo = onHdmiCecDeviceAdded(cecDeviceInfo);
+ case DO_ADD_HDMI_TV_INPUT: {
+ HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
+ TvInputInfo inputInfo = onHdmiDeviceAdded(deviceInfo);
if (inputInfo != null) {
- broadcastAddHdmiCecTvInput(cecDeviceInfo.getLogicalAddress(), inputInfo);
+ broadcastAddHdmiTvInput(deviceInfo.getLogicalAddress(), inputInfo);
}
return;
}
- case DO_REMOVE_HDMI_CEC_TV_INPUT: {
- HdmiDeviceInfo cecDeviceInfo = (HdmiDeviceInfo) msg.obj;
- String inputId = onHdmiCecDeviceRemoved(cecDeviceInfo);
+ case DO_REMOVE_HDMI_TV_INPUT: {
+ HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
+ String inputId = onHdmiDeviceRemoved(deviceInfo);
if (inputId != null) {
broadcastRemoveTvInput(inputId);
}
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
index ed1eeb9..ecba02a 100644
--- a/media/jni/android_media_MediaCodecList.cpp
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -21,6 +21,8 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaCodecList.h>
+#include <media/IMediaCodecList.h>
+#include <media/MediaCodecInfo.h>
#include "android_runtime/AndroidRuntime.h"
#include "jni.h"
@@ -29,20 +31,41 @@
using namespace android;
+static sp<IMediaCodecList> getCodecList(JNIEnv *env) {
+ sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
+ if (mcl == NULL) {
+ // This should never happen unless something is really wrong
+ jniThrowException(
+ env, "java/lang/RuntimeException", "cannot get MediaCodecList");
+ }
+ return mcl;
+}
+
static jint android_media_MediaCodecList_getCodecCount(
JNIEnv *env, jobject thiz) {
- return MediaCodecList::getInstance()->countCodecs();
+ sp<IMediaCodecList> mcl = getCodecList(env);
+ if (mcl == NULL) {
+ // Runtime exception already pending.
+ return 0;
+ }
+ return mcl->countCodecs();
}
static jstring android_media_MediaCodecList_getCodecName(
JNIEnv *env, jobject thiz, jint index) {
- const char *name = MediaCodecList::getInstance()->getCodecName(index);
+ sp<IMediaCodecList> mcl = getCodecList(env);
+ if (mcl == NULL) {
+ // Runtime exception already pending.
+ return NULL;
+ }
- if (name == NULL) {
+ const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
+ if (info == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return NULL;
}
+ const char *name = info->getCodecName();
return env->NewStringUTF(name);
}
@@ -54,33 +77,56 @@
}
const char *nameStr = env->GetStringUTFChars(name, NULL);
-
if (nameStr == NULL) {
// Out of memory exception already pending.
return -ENOENT;
}
- jint ret = MediaCodecList::getInstance()->findCodecByName(nameStr);
+ sp<IMediaCodecList> mcl = getCodecList(env);
+ if (mcl == NULL) {
+ // Runtime exception already pending.
+ return -ENOENT;
+ }
+
+ jint ret = mcl->findCodecByName(nameStr);
env->ReleaseStringUTFChars(name, nameStr);
return ret;
}
static jboolean android_media_MediaCodecList_isEncoder(
JNIEnv *env, jobject thiz, jint index) {
- return MediaCodecList::getInstance()->isEncoder(index);
+ sp<IMediaCodecList> mcl = getCodecList(env);
+ if (mcl == NULL) {
+ // Runtime exception already pending.
+ return false;
+ }
+
+ const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
+ if (info == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return false;
+ }
+
+ return info->isEncoder();
}
static jarray android_media_MediaCodecList_getSupportedTypes(
JNIEnv *env, jobject thiz, jint index) {
- Vector<AString> types;
- status_t err =
- MediaCodecList::getInstance()->getSupportedTypes(index, &types);
+ sp<IMediaCodecList> mcl = getCodecList(env);
+ if (mcl == NULL) {
+ // Runtime exception already pending.
+ return NULL;
+ }
- if (err != OK) {
+ const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
+ if (info == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return NULL;
}
+ Vector<AString> types;
+ info->getSupportedMimes(&types);
+
jclass clazz = env->FindClass("java/lang/String");
CHECK(clazz != NULL);
@@ -103,6 +149,18 @@
return NULL;
}
+ sp<IMediaCodecList> mcl = getCodecList(env);
+ if (mcl == NULL) {
+ // Runtime exception already pending.
+ return NULL;
+ }
+
+ const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
+ if (info == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return NULL;
+ }
+
const char *typeStr = env->GetStringUTFChars(type, NULL);
if (typeStr == NULL) {
@@ -110,30 +168,27 @@
return NULL;
}
- Vector<MediaCodecList::ProfileLevel> profileLevels;
+ Vector<MediaCodecInfo::ProfileLevel> profileLevels;
Vector<uint32_t> colorFormats;
- uint32_t flags;
- sp<AMessage> capabilities;
sp<AMessage> defaultFormat = new AMessage();
defaultFormat->setString("mime", typeStr);
// TODO query default-format also from codec/codec list
-
- status_t err =
- MediaCodecList::getInstance()->getCodecCapabilities(
- index, typeStr, &profileLevels, &colorFormats, &flags,
- &capabilities);
-
- bool isEncoder = MediaCodecList::getInstance()->isEncoder(index);
-
- env->ReleaseStringUTFChars(type, typeStr);
- typeStr = NULL;
-
- if (err != OK) {
+ const sp<MediaCodecInfo::Capabilities> &capabilities =
+ info->getCapabilitiesFor(typeStr);
+ if (capabilities == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return NULL;
}
+ env->ReleaseStringUTFChars(type, typeStr);
+ typeStr = NULL;
+
+ capabilities->getSupportedColorFormats(&colorFormats);
+ capabilities->getSupportedProfileLevels(&profileLevels);
+ uint32_t flags = capabilities->getFlags();
+ sp<AMessage> details = capabilities->getDetails();
+ bool isEncoder = info->isEncoder();
jobject defaultFormatObj = NULL;
if (ConvertMessageToMap(env, defaultFormat, &defaultFormatObj)) {
@@ -141,7 +196,7 @@
}
jobject infoObj = NULL;
- if (ConvertMessageToMap(env, capabilities, &infoObj)) {
+ if (ConvertMessageToMap(env, details, &infoObj)) {
env->DeleteLocalRef(defaultFormatObj);
return NULL;
}
@@ -164,7 +219,7 @@
env->GetFieldID(profileLevelClazz, "level", "I");
for (size_t i = 0; i < profileLevels.size(); ++i) {
- const MediaCodecList::ProfileLevel &src = profileLevels.itemAt(i);
+ const MediaCodecInfo::ProfileLevel &src = profileLevels.itemAt(i);
jobject profileLevelObj = env->AllocObject(profileLevelClazz);
@@ -192,26 +247,6 @@
profileLevelArray, colorFormatsArray, isEncoder, flags,
defaultFormatObj, infoObj);
-#if 0
- jfieldID profileLevelsField = env->GetFieldID(
- capsClazz,
- "profileLevels",
- "[Landroid/media/MediaCodecInfo$CodecProfileLevel;");
-
- env->SetObjectField(caps, profileLevelsField, profileLevelArray);
-
- jfieldID flagsField =
- env->GetFieldID(capsClazz, "mFlagsVerified", "I");
-
- env->SetIntField(caps, flagsField, flags);
-
- jfieldID colorFormatsField = env->GetFieldID(
- capsClazz, "colorFormats", "[I");
-
- env->SetObjectField(caps, colorFormatsField, colorFormatsArray);
-
-#endif
-
env->DeleteLocalRef(profileLevelArray);
profileLevelArray = NULL;
diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
index 6bb1f2c..e664fb9 100644
--- a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
+++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
@@ -26,38 +26,70 @@
*/
public class AppearAnimationUtils implements AppearAnimationCreator<View> {
- public static final long APPEAR_DURATION = 220;
+ public static final long DEFAULT_APPEAR_DURATION = 220;
- private final Interpolator mLinearOutSlowIn;
+ private final Interpolator mInterpolator;
private final float mStartTranslation;
private final AppearAnimationProperties mProperties = new AppearAnimationProperties();
private final float mDelayScale;
+ private final long mDuration;
public AppearAnimationUtils(Context ctx) {
- this(ctx, 1.0f, 1.0f);
+ this(ctx, DEFAULT_APPEAR_DURATION,
+ ctx.getResources().getDimensionPixelSize(R.dimen.appear_y_translation_start),
+ 1.0f,
+ AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in));
}
- public AppearAnimationUtils(Context ctx, float delayScaleFactor,
- float translationScaleFactor) {
- mLinearOutSlowIn = AnimationUtils.loadInterpolator(
- ctx, android.R.interpolator.linear_out_slow_in);
+ public AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor,
+ float delayScaleFactor, Interpolator interpolator) {
+ mInterpolator = interpolator;
mStartTranslation = ctx.getResources().getDimensionPixelOffset(
R.dimen.appear_y_translation_start) * translationScaleFactor;
mDelayScale = delayScaleFactor;
+ mDuration = duration;
}
public void startAppearAnimation(View[][] objects, final Runnable finishListener) {
startAppearAnimation(objects, finishListener, this);
}
+ public void startAppearAnimation(View[] objects, final Runnable finishListener) {
+ startAppearAnimation(objects, finishListener, this);
+ }
+
public <T> void startAppearAnimation(T[][] objects, final Runnable finishListener,
AppearAnimationCreator<T> creator) {
AppearAnimationProperties properties = getDelays(objects);
startAnimations(properties, objects, finishListener, creator);
}
+ public <T> void startAppearAnimation(T[] objects, final Runnable finishListener,
+ AppearAnimationCreator<T> creator) {
+ AppearAnimationProperties properties = getDelays(objects);
+ startAnimations(properties, objects, finishListener, creator);
+ }
+
+ private <T> void startAnimations(AppearAnimationProperties properties, T[] objects,
+ final Runnable finishListener, AppearAnimationCreator<T> creator) {
+ if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
+ finishListener.run();
+ return;
+ }
+ for (int row = 0; row < properties.delays.length; row++) {
+ long[] columns = properties.delays[row];
+ long delay = columns[0];
+ Runnable endRunnable = null;
+ if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == 0) {
+ endRunnable = finishListener;
+ }
+ creator.createAnimation(objects[row], delay, mDuration,
+ mStartTranslation, mInterpolator, endRunnable);
+ }
+ }
+
private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects,
- final Runnable finishListener, AppearAnimationCreator creator) {;
+ final Runnable finishListener, AppearAnimationCreator<T> creator) {
if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
finishListener.run();
return;
@@ -70,15 +102,32 @@
if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) {
endRunnable = finishListener;
}
- creator.createAnimation(objects[row][col], delay, APPEAR_DURATION,
- mStartTranslation, mLinearOutSlowIn, endRunnable);
+ creator.createAnimation(objects[row][col], delay, mDuration,
+ mStartTranslation, mInterpolator, endRunnable);
}
}
+ }
+ private <T> AppearAnimationProperties getDelays(T[] items) {
+ long maxDelay = -1;
+ mProperties.maxDelayColIndex = -1;
+ mProperties.maxDelayRowIndex = -1;
+ mProperties.delays = new long[items.length][];
+ for (int row = 0; row < items.length; row++) {
+ mProperties.delays[row] = new long[1];
+ long delay = calculateDelay(row, 0);
+ mProperties.delays[row][0] = delay;
+ if (items[row] != null && delay > maxDelay) {
+ maxDelay = delay;
+ mProperties.maxDelayColIndex = 0;
+ mProperties.maxDelayRowIndex = row;
+ }
+ }
+ return mProperties;
}
private <T> AppearAnimationProperties getDelays(T[][] items) {
- long maxDelay = 0;
+ long maxDelay = -1;
mProperties.maxDelayColIndex = -1;
mProperties.maxDelayRowIndex = -1;
mProperties.delays = new long[items.length][];
@@ -103,7 +152,7 @@
}
public Interpolator getInterpolator() {
- return mLinearOutSlowIn;
+ return mInterpolator;
}
public float getStartTranslation() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index d5dcd71..12bbd35 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -37,6 +37,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.LinearLayout;
@@ -108,8 +109,10 @@
public KeyguardPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- mAppearAnimationUtils = new AppearAnimationUtils(context, 1.5f /* delayScale */,
- 2.0f /* transitionScale */);
+ mAppearAnimationUtils = new AppearAnimationUtils(context,
+ AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 1.5f /* delayScale */,
+ 2.0f /* transitionScale */, AnimationUtils.loadInterpolator(
+ mContext, android.R.interpolator.linear_out_slow_in));
}
public void setKeyguardCallback(KeyguardSecurityCallback callback) {
@@ -420,7 +423,7 @@
this);
if (!TextUtils.isEmpty(mHelpMessage.getText())) {
mAppearAnimationUtils.createAnimation(mHelpMessage, 0,
- AppearAnimationUtils.APPEAR_DURATION,
+ AppearAnimationUtils.DEFAULT_APPEAR_DURATION,
mAppearAnimationUtils.getStartTranslation(),
mAppearAnimationUtils.getInterpolator(),
null /* finishRunnable */);
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher.xml b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
index 5648065..7c918c2 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
@@ -14,7 +14,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.systemui.statusbar.AlphaOptimizedLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/keyguard_user_switcher"
android:orientation="vertical"
android:layout_height="wrap_content"
@@ -22,4 +23,4 @@
android:gravity="end"
android:visibility="gone"
android:paddingTop="4dp">
-</LinearLayout>
+</com.android.systemui.statusbar.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 97efb47..51633dc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -768,13 +768,13 @@
<string name="disconnect_vpn">Disconnect VPN</string>
<!-- Monitoring dialog device owner body text [CHAR LIMIT=300] -->
- <string name="monitoring_description_device_owned">This device is managed by:\n<xliff:g id="organization">%1$s</xliff:g>\n\nYour administrator can monitor your network activity, including emails, apps and secure websites.\n\nFor more information, contact your administrator.</string>
+ <string name="monitoring_description_device_owned">This device is managed by:\n<xliff:g id="organization">%1$s</xliff:g>\n\nYour administrator can monitor your device and network activity, including emails, apps and secure websites.\n\nFor more information, contact your administrator.</string>
<!-- Monitoring dialog non-legacy VPN text [CHAR LIMIT=300] -->
- <string name="monitoring_description_vpn">You gave \"<xliff:g id="application">%1$s</xliff:g>\" permission to set up a VPN connection.\n\nThis app can monitor your network activity, including emails, apps and secure websites.</string>
+ <string name="monitoring_description_vpn">You gave \"<xliff:g id="application">%1$s</xliff:g>\" permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps and secure websites.</string>
<!-- Monitoring dialog legacy VPN text [CHAR LIMIT=300] -->
- <string name="monitoring_description_legacy_vpn">You\'re connected to a VPN (\"<xliff:g id="application">%1$s</xliff:g>\").\n\nYour VPN service provider can monitor your network activity including emails, apps, and secure websites.</string>
+ <string name="monitoring_description_legacy_vpn">You\'re connected to a VPN (\"<xliff:g id="application">%1$s</xliff:g>\").\n\nYour VPN service provider can monitor your device and network activity including emails, apps, and secure websites.</string>
<!-- Monitoring dialog non-legacy VPN with device owner text [CHAR LIMIT=300] -->
<string name="monitoring_description_vpn_device_owned">This device is managed by:\n<xliff:g id="organization">%1$s</xliff:g>\n\nYour administrator is capable of monitoring your network activity including emails, apps, and secure websites. For more information, contact your administrator.\n\nAlso, you gave \"<xliff:g id="application">%2$s</xliff:g>\" permission to set up a VPN connection. This app can monitor network activity too.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 759d540..a56b7a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -116,4 +116,9 @@
boolean activated = ArrayUtils.contains(getDrawableState(), android.R.attr.state_activated);
mName.setTypeface(activated ? mActivatedTypeface : mRegularTypeface);
}
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 98bdee0..2a782cc 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -738,12 +738,12 @@
}
}
- private void startApplicationDetailsActivity(String packageName) {
+ private void startApplicationDetailsActivity(String packageName, int userId) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null));
intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
TaskStackBuilder.create(getContext())
- .addNextIntentWithParentStack(intent).startActivities();
+ .addNextIntentWithParentStack(intent).startActivities(null, new UserHandle(userId));
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -769,7 +769,7 @@
ViewHolder viewHolder = (ViewHolder) selectedView.getTag();
if (viewHolder != null) {
final TaskDescription ad = viewHolder.taskDescription;
- startApplicationDetailsActivity(ad.packageName);
+ startApplicationDetailsActivity(ad.packageName, ad.userId);
show(false);
} else {
throw new IllegalStateException("Oops, no tag on view " + selectedView);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index a55c0f2..bb5fe54 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -328,7 +328,7 @@
TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm();
Rect taskStackBounds = new Rect(mTaskStackBounds);
taskStackBounds.bottom -= mSystemInsets.bottom;
- tsv.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
+ tsv.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds, mTriggeredFromAltTab);
tsv.getScroller().setStackScrollToInitialState();
// Find the running task in the TaskStack
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 1a32b81..34e8860 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -26,6 +26,7 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -385,7 +386,7 @@
sourceView = stackView;
transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
offsetX = transform.rect.left;
- offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
+ offsetY = mConfig.displayRect.height();
} else {
transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
}
@@ -474,7 +475,8 @@
Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
TaskStackBuilder.create(getContext())
- .addNextIntentWithParentStack(intent).startActivities();
+ .addNextIntentWithParentStack(intent).startActivities(null,
+ new UserHandle(t.userId));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 9e7dbf4..46996bb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -349,9 +349,9 @@
}
/** Updates the min and max virtual scroll bounds */
- void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
+ void updateMinMaxScroll(boolean boundScrollToNewMinMax, boolean launchedWithAltTab) {
// Compute the min and max scroll values
- mLayoutAlgorithm.computeMinMaxScroll(mStack.getTasks());
+ mLayoutAlgorithm.computeMinMaxScroll(mStack.getTasks(), launchedWithAltTab);
// Debug logging
if (boundScrollToNewMinMax) {
@@ -388,17 +388,17 @@
};
}
+ // Scroll the view into position (just center it in the curve)
if (scrollToNewPosition) {
- // Scroll the view into position
- // XXX: We probably want this to be centered in view instead of p = 0f
- float newScroll = mStackScroller.getBoundedStackScroll(
- mLayoutAlgorithm.getStackScrollForTaskIndex(t));
+ float newScroll = mLayoutAlgorithm.getStackScrollForTaskIndex(t) - 0.5f;
+ newScroll = mStackScroller.getBoundedStackScroll(newScroll);
mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll, postScrollRunnable);
} else {
if (postScrollRunnable != null) {
postScrollRunnable.run();
}
}
+
}
}
@@ -435,12 +435,13 @@
}
/** Computes the stack and task rects */
- public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
+ public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds,
+ boolean launchedWithAltTab) {
// Compute the rects in the stack algorithm
mLayoutAlgorithm.computeRects(windowWidth, windowHeight, taskStackBounds);
// Update the scroll bounds
- updateMinMaxScroll(false);
+ updateMinMaxScroll(false, launchedWithAltTab);
}
/**
@@ -455,7 +456,7 @@
// Compute our stack/task rects
Rect taskStackBounds = new Rect(mTaskStackBounds);
taskStackBounds.bottom -= mConfig.systemInsets.bottom;
- computeRects(width, height, taskStackBounds);
+ computeRects(width, height, taskStackBounds, mConfig.launchedWithAltTab);
// If this is the first layout, then scroll to the front of the stack and synchronize the
// stack views immediately to load all the views
@@ -543,9 +544,8 @@
mStartEnterAnimationContext = null;
}
- // Update the focused task index to be the next item to the top task
+ // When Alt-Tabbing, we scroll to and focus the previous task
if (mConfig.launchedWithAltTab) {
- // When alt-tabbing, we focus the next previous task
focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
}
}
@@ -661,7 +661,7 @@
mCb.onTaskViewDismissed(removedTask);
// Update the min/max scroll and animate other task views into their new positions
- updateMinMaxScroll(true);
+ updateMinMaxScroll(true, mConfig.launchedWithAltTab);
requestSynchronizeStackViewsWithModel(200);
// Update the new front most task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index b1482bb..633e6fa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -18,7 +18,6 @@
import android.graphics.Rect;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
@@ -87,8 +86,9 @@
left + size, mStackRect.top + size);
}
- /** Computes the minimum and maximum scroll progress values */
- void computeMinMaxScroll(ArrayList<Task> tasks) {
+ /** Computes the minimum and maximum scroll progress values. This method may be called before
+ * the RecentsConfiguration is set, so we need to pass in the alt-tab state. */
+ void computeMinMaxScroll(ArrayList<Task> tasks, boolean launchedWithAltTab) {
// Clear the progress map
mTaskProgressMap.clear();
@@ -130,7 +130,12 @@
mMinScrollP = 0f;
mMaxScrollP = pAtFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
- mInitialScrollP = pAtSecondFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
+ if (launchedWithAltTab) {
+ // Center the second most task, since that will be focused first
+ mInitialScrollP = pAtSecondFrontMostCardTop - 0.5f;
+ } else {
+ mInitialScrollP = pAtSecondFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
+ }
}
/** Update/get the transform */
@@ -151,6 +156,7 @@
// If the task top is outside of the bounds below the screen, then immediately reset it
if (pTaskRelative > 1f) {
transformOut.reset();
+ transformOut.rect.set(mTaskRect);
return transformOut;
}
// The check for the top is trickier, since we want to show the next task if it is at all
@@ -158,6 +164,7 @@
if (pTaskRelative < 0f) {
if (prevTransform != null && Float.compare(prevTransform.p, 0f) <= 0) {
transformOut.reset();
+ transformOut.rect.set(mTaskRect);
return transformOut;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 8996197..3b140823 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -82,6 +82,7 @@
public void hide(boolean destroyView) {
if (mKeyguardView != null) {
+ mKeyguardView.setOnDismissAction(null);
mKeyguardView.cleanUp();
}
if (destroyView) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index bf66c41..b66c310 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -16,10 +16,14 @@
package com.android.systemui.statusbar.phone;
+import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -48,6 +52,7 @@
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private int mSystemIconsSwitcherHiddenExpandedMargin;
+ private Interpolator mFastOutSlowInInterpolator;
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -61,6 +66,8 @@
mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar);
mBatteryLevel = (TextView) findViewById(R.id.battery_level);
loadDimens();
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
+ android.R.interpolator.fast_out_slow_in);
updateUserSwitcher();
}
@@ -70,7 +77,14 @@
}
private void updateVisibilities() {
- mMultiUserSwitch.setVisibility(!mKeyguardUserSwitcherShowing ? VISIBLE : GONE);
+ if (mMultiUserSwitch.getParent() != this && !mKeyguardUserSwitcherShowing) {
+ if (mMultiUserSwitch.getParent() != null) {
+ getOverlay().remove(mMultiUserSwitch);
+ }
+ addView(mMultiUserSwitch, 0);
+ } else if (mMultiUserSwitch.getParent() == this && mKeyguardUserSwitcherShowing) {
+ removeView(mMultiUserSwitch);
+ }
mBatteryLevel.setVisibility(mBatteryCharging ? View.VISIBLE : View.GONE);
}
@@ -137,12 +151,71 @@
updateUserSwitcher();
}
- public void setKeyguardUserSwitcherShowing(boolean showing) {
+ public void setKeyguardUserSwitcherShowing(boolean showing, boolean animate) {
mKeyguardUserSwitcherShowing = showing;
+ if (animate) {
+ animateNextLayoutChange();
+ }
updateVisibilities();
updateSystemIconsLayoutParams();
}
+ private void animateNextLayoutChange() {
+ final int systemIconsCurrentX = mSystemIconsSuperContainer.getLeft();
+ final boolean userSwitcherVisible = mMultiUserSwitch.getParent() == this;
+ getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ boolean userSwitcherHiding = userSwitcherVisible
+ && mMultiUserSwitch.getParent() != KeyguardStatusBarView.this;
+ mSystemIconsSuperContainer.setX(systemIconsCurrentX);
+ mSystemIconsSuperContainer.animate()
+ .translationX(0)
+ .setDuration(400)
+ .setStartDelay(userSwitcherHiding ? 300 : 0)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .start();
+ if (userSwitcherHiding) {
+ getOverlay().add(mMultiUserSwitch);
+ mMultiUserSwitch.animate()
+ .alpha(0f)
+ .setDuration(300)
+ .setStartDelay(0)
+ .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mMultiUserSwitch.setAlpha(1f);
+ getOverlay().remove(mMultiUserSwitch);
+ }
+ })
+ .start();
+
+ } else {
+ mMultiUserSwitch.setAlpha(0f);
+ mMultiUserSwitch.animate()
+ .alpha(1f)
+ .setDuration(300)
+ .setStartDelay(200)
+ .setInterpolator(PhoneStatusBar.ALPHA_IN);
+ }
+ return true;
+ }
+ });
+
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+ if (visibility != View.VISIBLE) {
+ mSystemIconsSuperContainer.animate().cancel();
+ mMultiUserSwitch.animate().cancel();
+ mMultiUserSwitch.setAlpha(1f);
+ }
+ }
+
@Override
public boolean hasOverlappingRendering() {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index af30266..47325c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -65,7 +65,7 @@
if (um.isUserSwitcherEnabled()) {
if (mKeyguardMode) {
if (mKeyguardUserSwitcher != null) {
- mKeyguardUserSwitcher.show();
+ mKeyguardUserSwitcher.show(true /* animate */);
}
} else {
mQsPanel.showDetailAdapter(true,
@@ -78,4 +78,9 @@
getContext().startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
}
}
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
}
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 e70422b..74ae4a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -950,7 +950,7 @@
? View.VISIBLE
: View.INVISIBLE);
if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
- mKeyguardUserSwitcher.hide();
+ mKeyguardUserSwitcher.hide(true /* animate */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 1d678af..75e31e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3341,7 +3341,7 @@
public void showKeyguard() {
setBarState(StatusBarState.KEYGUARD);
- updateKeyguardState(false /* goingToFullShade */);
+ updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
instantExpandNotificationsPanel();
mLeaveOpenOnKeyguardHide = false;
if (mDraggedDownRow != null) {
@@ -3413,7 +3413,7 @@
} else {
instantCollapseNotificationPanel();
}
- updateKeyguardState(staying);
+ updateKeyguardState(staying, false /* fromShadeLocked */);
return staying;
}
@@ -3449,14 +3449,15 @@
&& mStatusBarKeyguardViewManager.isSecure());
}
- private void updateKeyguardState(boolean goingToFullShade) {
+ private void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
if (mState == StatusBarState.KEYGUARD) {
mKeyguardIndicationController.setVisible(true);
mNotificationPanel.resetViews();
- mKeyguardUserSwitcher.setKeyguard(true);
+ mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
} else {
mKeyguardIndicationController.setVisible(false);
- mKeyguardUserSwitcher.setKeyguard(false);
+ mKeyguardUserSwitcher.setKeyguard(false,
+ goingToFullShade || mState == StatusBarState.SHADE_LOCKED || fromShadeLocked);
}
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
mScrimController.setKeyguardShowing(true);
@@ -3686,7 +3687,7 @@
} else {
mNotificationPanel.animateToFullShade(0 /* delay */);
setBarState(StatusBarState.SHADE_LOCKED);
- updateKeyguardState(false /* goingToFullShade */);
+ updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
if (row != null) {
row.setUserLocked(false);
}
@@ -3699,7 +3700,7 @@
public void goToKeyguard() {
if (mState == StatusBarState.SHADE_LOCKED) {
setBarState(StatusBarState.KEYGUARD);
- updateKeyguardState(false /* goingToFullShade */);
+ updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 203196e..18583ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -16,18 +16,25 @@
package com.android.systemui.statusbar.policy;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.animation.AnimationUtils;
import android.widget.TextView;
+import com.android.keyguard.AppearAnimationUtils;
import com.android.systemui.R;
import com.android.systemui.qs.tiles.UserDetailItemView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.StatusBarHeaderView;
import com.android.systemui.statusbar.phone.UserAvatarView;
@@ -43,33 +50,42 @@
private final KeyguardStatusBarView mStatusBarView;
private final Adapter mAdapter;
private final boolean mSimpleUserSwitcher;
+ private final AppearAnimationUtils mAppearAnimationUtils;
+ private final KeyguardUserSwitcherScrim mBackground;
+ private ObjectAnimator mBgAnimator;
public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
KeyguardStatusBarView statusBarView, NotificationPanelView panelView,
UserSwitcherController userSwitcherController) {
if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON) {
mUserSwitcher = (ViewGroup) userSwitcher.inflate();
- mUserSwitcher.setBackground(new KeyguardUserSwitcherScrim(mUserSwitcher));
+ mBackground = new KeyguardUserSwitcherScrim(mUserSwitcher);
+ mUserSwitcher.setBackground(mBackground);
mStatusBarView = statusBarView;
mStatusBarView.setKeyguardUserSwitcher(this);
panelView.setKeyguardUserSwitcher(this);
mAdapter = new Adapter(context, userSwitcherController);
mAdapter.registerDataSetObserver(mDataSetObserver);
mSimpleUserSwitcher = userSwitcherController.isSimpleUserSwitcher();
+ mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f,
+ AnimationUtils.loadInterpolator(
+ context, android.R.interpolator.fast_out_slow_in));
} else {
mUserSwitcher = null;
mStatusBarView = null;
mAdapter = null;
mSimpleUserSwitcher = false;
+ mAppearAnimationUtils = null;
+ mBackground = null;
}
}
- public void setKeyguard(boolean keyguard) {
+ public void setKeyguard(boolean keyguard, boolean animate) {
if (mUserSwitcher != null) {
if (keyguard && shouldExpandByDefault()) {
- show();
+ show(animate);
} else {
- hide();
+ hide(animate);
}
}
}
@@ -82,22 +98,81 @@
return mSimpleUserSwitcher;
}
- public void show() {
- if (mUserSwitcher != null) {
- // TODO: animate
+ public void show(boolean animate) {
+ if (mUserSwitcher != null && mUserSwitcher.getVisibility() != View.VISIBLE) {
+ cancelAnimations();
mUserSwitcher.setVisibility(View.VISIBLE);
- mStatusBarView.setKeyguardUserSwitcherShowing(true);
+ mStatusBarView.setKeyguardUserSwitcherShowing(true, animate);
+ if (animate) {
+ startAppearAnimation();
+ }
}
}
- public void hide() {
+ public void hide(boolean animate) {
if (mUserSwitcher != null && mUserSwitcher.getVisibility() == View.VISIBLE) {
- // TODO: animate
- mUserSwitcher.setVisibility(View.GONE);
- mStatusBarView.setKeyguardUserSwitcherShowing(false);
+ cancelAnimations();
+ if (animate) {
+ startDisappearAnimation();
+ } else {
+ mUserSwitcher.setVisibility(View.GONE);
+ }
+ mStatusBarView.setKeyguardUserSwitcherShowing(false, animate);
}
}
+ private void cancelAnimations() {
+ int count = mUserSwitcher.getChildCount();
+ for (int i = 0; i < count; i++) {
+ mUserSwitcher.getChildAt(i).animate().cancel();
+ }
+ if (mBgAnimator != null) {
+ mBgAnimator.cancel();
+ }
+ mUserSwitcher.animate().cancel();
+ }
+
+ private void startAppearAnimation() {
+ int count = mUserSwitcher.getChildCount();
+ View[] objects = new View[count];
+ for (int i = 0; i < count; i++) {
+ objects[i] = mUserSwitcher.getChildAt(i);
+ }
+ mUserSwitcher.setClipChildren(false);
+ mUserSwitcher.setClipToPadding(false);
+ mAppearAnimationUtils.startAppearAnimation(objects, new Runnable() {
+ @Override
+ public void run() {
+ mUserSwitcher.setClipChildren(true);
+ mUserSwitcher.setClipToPadding(true);
+ }
+ });
+ mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
+ mBgAnimator.setDuration(400);
+ mBgAnimator.setInterpolator(PhoneStatusBar.ALPHA_IN);
+ mBgAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBgAnimator = null;
+ }
+ });
+ mBgAnimator.start();
+ }
+
+ private void startDisappearAnimation() {
+ mUserSwitcher.animate()
+ .alpha(0f)
+ .setDuration(300)
+ .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mUserSwitcher.setVisibility(View.GONE);
+ mUserSwitcher.setAlpha(1f);
+ }
+ });
+ }
+
private void refresh() {
final int childCount = mUserSwitcher.getChildCount();
final int adapterCount = mAdapter.getCount();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
index 3356afd..4363037 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
@@ -19,6 +19,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
+import android.graphics.LightingColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.RadialGradient;
@@ -41,7 +42,9 @@
private int mDarkColor;
private int mTop;
+ private int mAlpha;
private Paint mRadialGradientPaint = new Paint();
+ private int mLayoutWidth;
public KeyguardUserSwitcherScrim(View host) {
host.addOnLayoutChangeListener(this);
@@ -56,13 +59,21 @@
float width = bounds.width() * OUTER_EXTENT;
float height = (mTop + bounds.height()) * OUTER_EXTENT;
canvas.translate(0, -mTop);
- canvas.scale(1, height/width);
+ canvas.scale(1, height / width);
canvas.drawRect(isLtr ? bounds.right - width : 0, 0,
isLtr ? bounds.right : bounds.left + width, width, mRadialGradientPaint);
}
@Override
public void setAlpha(int alpha) {
+ mAlpha = alpha;
+ updatePaint();
+ invalidateSelf();
+ }
+
+ @Override
+ public int getAlpha() {
+ return mAlpha;
}
@Override
@@ -78,15 +89,24 @@
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
- int width = right - left;
- float radius = width * OUTER_EXTENT;
- boolean isLtr = getLayoutDirection() == LayoutDirection.LTR;
- mRadialGradientPaint.setShader(
- new RadialGradient(isLtr ? width : 0, 0, radius,
- new int[] { mDarkColor, Color.TRANSPARENT},
- new float[] { Math.max(0f, width * INNER_EXTENT / radius), 1f},
- Shader.TileMode.CLAMP));
+ mLayoutWidth = right - left;
mTop = top;
+ updatePaint();
}
}
+
+ private void updatePaint() {
+ if (mLayoutWidth == 0) {
+ return;
+ }
+ float radius = mLayoutWidth * OUTER_EXTENT;
+ boolean isLtr = getLayoutDirection() == LayoutDirection.LTR;
+ mRadialGradientPaint.setShader(
+ new RadialGradient(isLtr ? mLayoutWidth : 0, 0, radius,
+ new int[] { Color.argb(
+ (int) (Color.alpha(mDarkColor) * mAlpha / 255f), 0, 0, 0),
+ Color.TRANSPARENT },
+ new float[] { Math.max(0f, mLayoutWidth * INNER_EXTENT / radius), 1f },
+ Shader.TileMode.CLAMP));
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index ff3cd9d..964acbd 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1314,7 +1314,6 @@
mBackgroundDrawable = drawable;
if (mDecor != null) {
mDecor.setWindowBackground(drawable);
- mDecor.setClipToOutline(drawable != null && mClipToOutline);
}
}
}
@@ -3389,10 +3388,6 @@
}
mDecor.setWindowBackground(background);
- if (background != null) {
- mDecor.setClipToOutline(mClipToOutline);
- }
-
final Drawable frame;
if (mFrameResource != 0) {
frame = getContext().getDrawable(mFrameResource);
@@ -3402,6 +3397,7 @@
mDecor.setWindowFrame(frame);
mDecor.setElevation(mElevation);
+ mDecor.setClipToOutline(mClipToOutline);
if (mTitle != null) {
setTitle(mTitle);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 59aef32..77b14ac 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -5129,6 +5129,14 @@
}
}
+ // The path needs to be canonical
+ if (info.path.contains("..") || info.path.contains("//")) {
+ if (MORE_DEBUG) {
+ Slog.w(TAG, "Dropping invalid path " + info.path);
+ }
+ return false;
+ }
+
// Otherwise we think this file is good to go
return true;
}
@@ -5680,6 +5688,14 @@
break;
}
+ // The path needs to be canonical
+ if (info.path.contains("..") || info.path.contains("//")) {
+ if (MORE_DEBUG) {
+ Slog.w(TAG, "Dropping invalid path " + info.path);
+ }
+ okay = false;
+ }
+
// If the policy is satisfied, go ahead and set up to pipe the
// data to the agent.
if (DEBUG && okay && mAgent != null) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6761f24..f9baccd 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -251,10 +251,7 @@
private int mNetworkPreference;
private int mActiveDefaultNetwork = -1;
// 0 is full bad, 100 is full good
- private int mDefaultInetCondition = 0;
private int mDefaultInetConditionPublished = 0;
- private boolean mInetConditionChangeInFlight = false;
- private int mDefaultConnectionSequence = 0;
private Object mDnsLock = new Object();
private int mNumDnsEntries;
@@ -274,19 +271,6 @@
private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = 2;
/**
- * used internally to synchronize inet condition reports
- * arg1 = networkType
- * arg2 = condition (0 bad, 100 good)
- */
- private static final int EVENT_INET_CONDITION_CHANGE = 4;
-
- /**
- * used internally to mark the end of inet condition hold periods
- * arg1 = networkType
- */
- private static final int EVENT_INET_CONDITION_HOLD_END = 5;
-
- /**
* used internally to clear a wakelock when transitioning
* from one net to another. Clear happens when we get a new
* network - EVENT_EXPIRE_NET_TRANSITION_WAKELOCK happens
@@ -490,10 +474,6 @@
mTypeLists[type] = new ArrayList<NetworkAgentInfo>();
}
- private boolean isDefaultNetwork(NetworkAgentInfo nai) {
- return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai;
- }
-
public boolean isTypeSupported(int type) {
return isNetworkTypeValid(type) && mTypeLists[type] != null;
}
@@ -2052,6 +2032,9 @@
nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
null, null);
}
+ if (isDefaultNetwork(nai)) {
+ mDefaultInetConditionPublished = 0;
+ }
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
@@ -2222,18 +2205,6 @@
}
break;
}
- case EVENT_INET_CONDITION_CHANGE: {
- int netType = msg.arg1;
- int condition = msg.arg2;
- handleInetConditionChange(netType, condition);
- break;
- }
- case EVENT_INET_CONDITION_HOLD_END: {
- int netType = msg.arg1;
- int sequence = msg.arg2;
- handleInetConditionHoldEnd(netType, sequence);
- break;
- }
case EVENT_APPLY_GLOBAL_HTTP_PROXY: {
handleDeprecatedGlobalHttpProxy();
break;
@@ -2428,99 +2399,15 @@
// 100 percent is full good, 0 is full bad.
public void reportInetCondition(int networkType, int percentage) {
- if (VDBG) log("reportNetworkCondition(" + networkType + ", " + percentage + ")");
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.STATUS_BAR,
- "ConnectivityService");
-
- if (DBG) {
- int pid = getCallingPid();
- int uid = getCallingUid();
- String s = pid + "(" + uid + ") reports inet is " +
- (percentage > 50 ? "connected" : "disconnected") + " (" + percentage + ") on " +
- "network Type " + networkType + " at " + GregorianCalendar.getInstance().getTime();
- mInetLog.add(s);
- while(mInetLog.size() > INET_CONDITION_LOG_MAX_SIZE) {
- mInetLog.remove(0);
- }
- }
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_INET_CONDITION_CHANGE, networkType, percentage));
+ if (percentage > 50) return; // don't handle good network reports
+ NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ if (nai != null) reportBadNetwork(nai.network);
}
public void reportBadNetwork(Network network) {
//TODO
}
- private void handleInetConditionChange(int netType, int condition) {
- if (mActiveDefaultNetwork == -1) {
- if (DBG) log("handleInetConditionChange: no active default network - ignore");
- return;
- }
- if (mActiveDefaultNetwork != netType) {
- if (DBG) log("handleInetConditionChange: net=" + netType +
- " != default=" + mActiveDefaultNetwork + " - ignore");
- return;
- }
- if (VDBG) {
- log("handleInetConditionChange: net=" +
- netType + ", condition=" + condition +
- ",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
- }
- mDefaultInetCondition = condition;
- int delay;
- if (mInetConditionChangeInFlight == false) {
- if (VDBG) log("handleInetConditionChange: starting a change hold");
- // setup a new hold to debounce this
- if (mDefaultInetCondition > 50) {
- delay = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.INET_CONDITION_DEBOUNCE_UP_DELAY, 500);
- } else {
- delay = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000);
- }
- mInetConditionChangeInFlight = true;
- mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_INET_CONDITION_HOLD_END,
- mActiveDefaultNetwork, mDefaultConnectionSequence), delay);
- } else {
- // we've set the new condition, when this hold ends that will get picked up
- if (VDBG) log("handleInetConditionChange: currently in hold - not setting new end evt");
- }
- }
-
- private void handleInetConditionHoldEnd(int netType, int sequence) {
- if (DBG) {
- log("handleInetConditionHoldEnd: net=" + netType +
- ", condition=" + mDefaultInetCondition +
- ", published condition=" + mDefaultInetConditionPublished);
- }
- mInetConditionChangeInFlight = false;
-
- if (mActiveDefaultNetwork == -1) {
- if (DBG) log("handleInetConditionHoldEnd: no active default network - ignoring");
- return;
- }
- if (mDefaultConnectionSequence != sequence) {
- if (DBG) log("handleInetConditionHoldEnd: event hold for obsolete network - ignoring");
- return;
- }
- // TODO: Figure out why this optimization sometimes causes a
- // change in mDefaultInetCondition to be missed and the
- // UI to not be updated.
- //if (mDefaultInetConditionPublished == mDefaultInetCondition) {
- // if (DBG) log("no change in condition - aborting");
- // return;
- //}
- NetworkInfo networkInfo = getNetworkInfoForType(mActiveDefaultNetwork);
- if (networkInfo.isConnected() == false) {
- if (DBG) log("handleInetConditionHoldEnd: default network not connected - ignoring");
- return;
- }
- mDefaultInetConditionPublished = mDefaultInetCondition;
- sendInetConditionBroadcast(networkInfo);
- return;
- }
-
public ProxyInfo getProxy() {
// this information is already available as a world read/writable jvm property
// so this API change wouldn't have a benifit. It also breaks the passing
@@ -4206,6 +4093,10 @@
private final NetworkRequest mDefaultRequest;
+ private boolean isDefaultNetwork(NetworkAgentInfo nai) {
+ return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai;
+ }
+
public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int currentScore, NetworkMisc networkMisc) {
@@ -4532,6 +4423,7 @@
mLegacyTypeTracker.remove(currentNetwork.networkInfo.getType(),
currentNetwork);
}
+ mDefaultInetConditionPublished = 100;
mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
}
}
@@ -4581,8 +4473,6 @@
// to connected after our normal pause unless somebody reports us as
// really disconnected
mDefaultInetConditionPublished = 0;
- mDefaultConnectionSequence++;
- mInetConditionChangeInFlight = false;
// TODO - read the tcp buffer size config string from somewhere
// updateNetworkSettings();
}
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index af38664..d05c280 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -201,9 +201,6 @@
// There are many components in the system watching for this so as to
// adjust audio routing, screen orientation, etc.
getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-
- // Release the wake lock that was acquired when the message was posted.
- mWakeLock.release();
}
}
@@ -213,6 +210,7 @@
switch (msg.what) {
case MSG_DOCK_STATE_CHANGED:
handleDockStateChange();
+ mWakeLock.release();
break;
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2dd150a..e31f177 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1269,28 +1269,26 @@
+ "greater than 0");
}
+ if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+ }
+ if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
+ flags &= ~DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+ }
+
if (projection != null) {
try {
if (!getProjectionService().isValidMediaProjection(projection)) {
throw new SecurityException("Invalid media projection");
}
+ flags = projection.applyVirtualDisplayFlags(flags);
} catch (RemoteException e) {
- throw new SecurityException("unable to validate media projection");
- }
- flags &= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
- try {
- flags |= projection.getVirtualDisplayFlags();
- } catch (RemoteException e) {
- throw new RuntimeException("unable to retrieve media projection flags");
+ throw new SecurityException("unable to validate media projection or flags");
}
}
- if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE) != 0) {
- if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0 ||
- (flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) {
- throw new IllegalArgumentException("screen sharing virtual displays must not "
- + "be public or presentation displays");
- }
+ if (callingUid != Process.SYSTEM_UID &&
+ (flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
if (!canProjectVideo(projection)) {
throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
+ "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
@@ -1298,16 +1296,6 @@
+ "display.");
}
}
-
-
- if (callingUid != Process.SYSTEM_UID &&
- (flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
- if (!canProjectVideo(projection)) {
- throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
- + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
- + "MediaProjection token to create a public virtual display.");
- }
- }
if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
if (!canProjectSecureVideo(projection)) {
throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT "
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 0ebd2de..72ac29a 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -262,14 +262,15 @@
mInfo.presentationDeadlineNanos = 1000000000L / (int) mInfo.refreshRate; // 1 frame
mInfo.flags = 0;
if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
- mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE;
- if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE) == 0) {
- mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY
- | DisplayDeviceInfo.FLAG_NEVER_BLANK;
- }
- } else if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
+ | DisplayDeviceInfo.FLAG_NEVER_BLANK;
+ }
+ if ((mInfo.flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
+ mInfo.flags &= ~DisplayDeviceInfo.FLAG_NEVER_BLANK;
+ } else {
mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
}
+
if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index fb4fa7f..ad5b2ba 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -617,6 +617,7 @@
}
addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
+ addAndStartAction(new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));
// If there is AVR, initiate System Audio Auto initiation action,
// which turns on and off system audio according to last system
@@ -1300,6 +1301,7 @@
// LocalDeviceTv.onAddressAllocated() -> launchDeviceDiscovery().
removeAction(DeviceDiscoveryAction.class);
removeAction(HotplugDetectionAction.class);
+ removeAction(PowerStatusMonitorAction.class);
// Remove recording actions.
removeAction(OneTouchRecordAction.class);
removeAction(TimerRecordingAction.class);
@@ -1521,4 +1523,22 @@
}
});
}
+
+ void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
+ HdmiDeviceInfo info = getDeviceInfo(logicalAddress);
+ if (info == null) {
+ Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
+ return;
+ }
+
+ if (info.getDevicePowerStatus() == newPowerStatus) {
+ return;
+ }
+
+ HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus);
+ // addDeviceInfo replaces old device info with new one if exists.
+ addDeviceInfo(newInfo);
+
+ // TODO: notify this update to others.
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index cccc44c..81b99f0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -317,7 +317,10 @@
if (logicalAddress == Constants.ADDR_UNREGISTERED) {
Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
} else {
- HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
+ // Set POWER_STATUS_ON to all local devices because they share lifetime
+ // with system.
+ HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType,
+ HdmiControlManager.POWER_STATUS_ON);
localDevice.setDeviceInfo(deviceInfo);
mCecController.addLocalDevice(deviceType, localDevice);
mCecController.addLogicalAddress(logicalAddress);
@@ -653,7 +656,7 @@
}
}
- private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
+ private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) {
// TODO: find better name instead of model name.
String displayName = Build.MODEL;
return new HdmiDeviceInfo(logicalAddress,
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index a52e0d2..23f19ff 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -266,4 +266,14 @@
}
return true;
}
+
+ /**
+ * Clone {@link HdmiDeviceInfo} with new power status.
+ */
+ static HdmiDeviceInfo cloneHdmiDeviceInfo(HdmiDeviceInfo info, int newPowerStatus) {
+ return new HdmiDeviceInfo(info.getLogicalAddress(),
+ info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(),
+ info.getVendorId(), info.getDisplayName(), newPowerStatus);
+ }
+
}
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
new file mode 100644
index 0000000..03fbb95
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
@@ -0,0 +1,146 @@
+/*
+ * 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.hdmi;
+
+import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_UNKNOWN;
+
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.util.SparseIntArray;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
+import java.util.List;
+
+/**
+ * Action that check each device's power status.
+ */
+public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
+ private static final String TAG = "PowerStatusMonitorAction";
+
+ // State that waits for <Report Power Status> once sending <Give Device Power Status>
+ // to all external devices.
+ private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1;
+ // State that waits for next monitoring
+ private static final int STATE_WAIT_FOR_NEXT_MONITORING = 2;
+
+ private static final int INVALID_POWER_STATUS = POWER_STATUS_UNKNOWN - 1;
+
+ // Monitoring interval (60s)
+ private static final int MONITIROING_INTERNAL_MS = 60000;
+
+ // Timeout once sending <Give Device Power Status>
+ private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000;
+
+ // Container for current power status of all external devices.
+ // The key is a logical address a device and the value is current power status of it
+ // Whenever the action receives <Report Power Status> from a device,
+ // it removes an entry of the given device.
+ // If this is non-empty when timeout for STATE_WAIT_FOR_REPORT_POWER_STATUS happens,
+ // updates power status of all remaining devices into POWER_STATUS_UNKNOWN.
+ private final SparseIntArray mPowerStatus = new SparseIntArray();
+
+ PowerStatusMonitorAction(HdmiCecLocalDevice source) {
+ super(source);
+ }
+
+ @Override
+ boolean start() {
+ queryPowerStatus();
+ return true;
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAIT_FOR_REPORT_POWER_STATUS) {
+ return false;
+ }
+ return handleReportPowerStatus(cmd);
+ }
+
+ private boolean handleReportPowerStatus(HdmiCecMessage cmd) {
+ int sourceAddress = cmd.getSource();
+ int oldStatus = mPowerStatus.get(sourceAddress, INVALID_POWER_STATUS);
+ if (oldStatus == INVALID_POWER_STATUS) {
+ // if no device exists for incoming message, hands it over to other actions.
+ return false;
+ }
+ int newStatus = cmd.getParams()[0];
+ updatePowerStatus(sourceAddress, newStatus, true);
+ return true;
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ switch (mState) {
+ case STATE_WAIT_FOR_NEXT_MONITORING:
+ queryPowerStatus();
+ break;
+ case STATE_WAIT_FOR_REPORT_POWER_STATUS:
+ handleTimeout();
+ break;
+ }
+ }
+
+ private void handleTimeout() {
+ for (int i = 0; i < mPowerStatus.size(); ++i) {
+ int logicalAddress = mPowerStatus.keyAt(i);
+ updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, false);
+ }
+ mPowerStatus.clear();
+ mState = STATE_WAIT_FOR_NEXT_MONITORING;
+ }
+
+ private void resetPowerStatus(List<HdmiDeviceInfo> deviceInfos) {
+ mPowerStatus.clear();
+ for (HdmiDeviceInfo info : deviceInfos) {
+ mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus());
+ }
+ }
+
+ private void queryPowerStatus() {
+ List<HdmiDeviceInfo> deviceInfos = tv().getDeviceInfoList(false);
+ resetPowerStatus(deviceInfos);
+ for (HdmiDeviceInfo info : deviceInfos) {
+ final int logicalAddress = info.getLogicalAddress();
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+ logicalAddress),
+ new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ // If fails to send <Give Device Power Status>,
+ // update power status into UNKNOWN.
+ if (error != Constants.SEND_RESULT_SUCCESS) {
+ updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true);
+ }
+ }
+ });
+ }
+
+ mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
+
+ // Add both timers, monitoring and timeout.
+ addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITIROING_INTERNAL_MS);
+ addTimer(STATE_WAIT_FOR_REPORT_POWER_STATUS, REPORT_POWER_STATUS_TIMEOUT_MS);
+ }
+
+ private void updatePowerStatus(int logicalAddress, int newStatus, boolean remove) {
+ tv().updateDevicePowerStatus(logicalAddress, newStatus);
+
+ if (remove) {
+ mPowerStatus.delete(logicalAddress);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 7b28699..289b5aa 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -103,7 +103,7 @@
try {
hasPermission |= checkPermission(packageName,
android.Manifest.permission.CAPTURE_VIDEO_OUTPUT)
- || mAppOps.checkOpNoThrow(
+ || mAppOps.noteOpNoThrow(
AppOpsManager.OP_PROJECT_MEDIA, uid, packageName)
== AppOpsManager.MODE_ALLOWED;
} finally {
@@ -196,18 +196,27 @@
}
@Override // Binder call
- public int getVirtualDisplayFlags() {
- switch (mType) {
- case MediaProjectionManager.TYPE_SCREEN_CAPTURE:
- return DisplayManager.VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE;
- case MediaProjectionManager.TYPE_MIRRORING:
- return DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
- DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
- case MediaProjectionManager.TYPE_PRESENTATION:
- return DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION |
- DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+ public int applyVirtualDisplayFlags(int flags) {
+ if (mType == MediaProjectionManager.TYPE_SCREEN_CAPTURE) {
+ flags &= ~DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+ return flags;
+ } else if (mType == MediaProjectionManager.TYPE_MIRRORING) {
+ flags &= ~(DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR);
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY |
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+ return flags;
+ } else if (mType == MediaProjectionManager.TYPE_PRESENTATION) {
+ flags &= ~DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION |
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+ return flags;
+ } else {
+ throw new RuntimeException("Unknown MediaProjection type");
}
- throw new RuntimeException("Unknown MediaProjection type");
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index b4faea1..c7e3fb7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -37,15 +37,17 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
-import android.content.pm.IPackageDeleteObserver;
+import android.content.Intent;
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageInstallerSession;
import android.content.pm.InstallSessionInfo;
import android.content.pm.InstallSessionParams;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
@@ -199,6 +201,10 @@
}
}
+ public static boolean isStageFile(File file) {
+ return sStageFilter.accept(null, file.getName());
+ }
+
@Deprecated
public File allocateSessionDir() throws IOException {
synchronized (mSessions) {
@@ -545,8 +551,21 @@
int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstall");
- // TODO: enforce installer of record or permission
- mPm.deletePackage(packageName, observer, userId, flags);
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
+ == PackageManager.PERMISSION_GRANTED) {
+ // Sweet, call straight through!
+ mPm.deletePackage(packageName, observer, userId, flags);
+
+ } else {
+ // Take a short detour to confirm with user
+ final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
+ intent.setData(Uri.fromParts("package", packageName, null));
+ intent.putExtra(PackageInstaller.EXTRA_CALLBACK, observer.asBinder());
+ try {
+ observer.onUserActionRequired(intent);
+ } catch (RemoteException ignored) {
+ }
+ }
}
@Override
@@ -559,6 +578,15 @@
}
@Override
+ public void setPermissionsResult(int sessionId, boolean accepted) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
+
+ synchronized (mSessions) {
+ mSessions.get(sessionId).setPermissionsResult(accepted);
+ }
+ }
+
+ @Override
public void registerCallback(IPackageInstallerCallback callback, int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "registerCallback");
enforceCallerCanReadSessions();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5443fbc..a3184f0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
@@ -30,6 +31,7 @@
import android.content.pm.IPackageInstallerSession;
import android.content.pm.InstallSessionInfo;
import android.content.pm.InstallSessionParams;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ApkLite;
@@ -106,10 +108,24 @@
@GuardedBy("mLock")
private boolean mSealed = false;
@GuardedBy("mLock")
- private boolean mPermissionsConfirmed = false;
+ private boolean mPermissionsAccepted = false;
@GuardedBy("mLock")
private boolean mDestroyed = false;
+ private int mFinalStatus;
+ private String mFinalMessage;
+
+ /**
+ * Path to the resolved base APK for this session, which may point at an APK
+ * inside the session (when the session defines the base), or it may point
+ * at the existing base APK (when adding splits to an existing app).
+ * <p>
+ * This is used when confirming permissions, since we can't fully stage the
+ * session inside an ASEC before confirming with user.
+ */
+ @GuardedBy("mLock")
+ private String mResolvedBaseCodePath;
+
@GuardedBy("mLock")
private ArrayList<FileBridge> mBridges = new ArrayList<>();
@@ -134,12 +150,7 @@
} catch (PackageManagerException e) {
Slog.e(TAG, "Install failed: " + e);
destroyInternal();
- try {
- mRemoteObserver.onPackageInstalled(mPackageName, e.error, e.getMessage(),
- null);
- } catch (RemoteException ignored) {
- }
- mCallback.onSessionFinished(PackageInstallerSession.this, false);
+ dispatchSessionFinished(e.error, e.getMessage(), null);
}
return true;
@@ -169,9 +180,9 @@
if (mPm.checkPermission(android.Manifest.permission.INSTALL_PACKAGES,
installerPackageName) == PackageManager.PERMISSION_GRANTED) {
- mPermissionsConfirmed = true;
+ mPermissionsAccepted = true;
} else {
- mPermissionsConfirmed = false;
+ mPermissionsAccepted = false;
}
computeProgressLocked();
@@ -182,7 +193,9 @@
info.sessionId = sessionId;
info.installerPackageName = installerPackageName;
+ info.resolvedBaseCodePath = mResolvedBaseCodePath;
info.progress = mProgress;
+ info.sealed = mSealed;
info.open = openCount.get() > 0;
info.mode = params.mode;
@@ -355,11 +368,19 @@
Preconditions.checkNotNull(mPackageName);
Preconditions.checkNotNull(mSignatures);
+ Preconditions.checkNotNull(mResolvedBaseCodePath);
- if (!mPermissionsConfirmed) {
- // TODO: async confirm permissions with user
- // when they confirm, we'll kick off another install() pass
- throw new SecurityException("Caller must hold INSTALL permission");
+ if (!mPermissionsAccepted) {
+ // User needs to accept permissions; give installer an intent they
+ // can use to involve user.
+ final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
+ intent.setPackage("com.android.packageinstaller");
+ intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+ try {
+ mRemoteObserver.onUserActionRequired(intent);
+ } catch (RemoteException ignored) {
+ }
+ return;
}
// Inherit any packages and native libraries from existing install that
@@ -386,12 +407,7 @@
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
destroyInternal();
- try {
- remoteObserver.onPackageInstalled(basePackageName, returnCode, msg, extras);
- } catch (RemoteException ignored) {
- }
- final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
- mCallback.onSessionFinished(PackageInstallerSession.this, success);
+ dispatchSessionFinished(returnCode, msg, extras);
}
};
@@ -409,6 +425,7 @@
mPackageName = null;
mVersionCode = -1;
mSignatures = null;
+ mResolvedBaseCodePath = null;
final File[] files = sessionStageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
@@ -445,18 +462,25 @@
info.signatures);
// Take this opportunity to enforce uniform naming
- final String name;
+ final String targetName;
if (info.splitName == null) {
- name = "base.apk";
+ targetName = "base.apk";
} else {
- name = "split_" + info.splitName + ".apk";
+ targetName = "split_" + info.splitName + ".apk";
}
- if (!FileUtils.isValidExtFilename(name)) {
+ if (!FileUtils.isValidExtFilename(targetName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Invalid filename: " + name);
+ "Invalid filename: " + targetName);
}
- if (!file.getName().equals(name)) {
- file.renameTo(new File(file.getParentFile(), name));
+
+ final File targetFile = new File(sessionStageDir, targetName);
+ if (!file.equals(targetFile)) {
+ file.renameTo(targetFile);
+ }
+
+ // Base is coming from session
+ if (info.splitName == null) {
+ mResolvedBaseCodePath = targetFile.getAbsolutePath();
}
}
@@ -472,13 +496,18 @@
}
} else {
- // Partial installs must be consistent with existing install.
+ // Partial installs must be consistent with existing install
final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
if (app == null) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Missing existing base package for " + mPackageName);
}
+ // Base might be inherited from existing install
+ if (mResolvedBaseCodePath == null) {
+ mResolvedBaseCodePath = app.getBaseCodePath();
+ }
+
final ApkLite info;
try {
info = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
@@ -537,6 +566,21 @@
if (LOGD) Slog.d(TAG, "Spliced " + n + " existing APKs into stage");
}
+ void setPermissionsResult(boolean accepted) {
+ if (!mSealed) {
+ throw new SecurityException("Must be sealed to accept permissions");
+ }
+
+ if (accepted) {
+ // Mark and kick off another install pass
+ mPermissionsAccepted = true;
+ mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
+ } else {
+ destroyInternal();
+ dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
+ }
+ }
+
@Override
public void close() {
if (openCount.decrementAndGet() == 0) {
@@ -546,11 +590,23 @@
@Override
public void abandon() {
- try {
- destroyInternal();
- } finally {
- mCallback.onSessionFinished(this, false);
+ destroyInternal();
+ dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
+ }
+
+ private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
+ mFinalStatus = returnCode;
+ mFinalMessage = msg;
+
+ if (mRemoteObserver != null) {
+ try {
+ mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras);
+ } catch (RemoteException ignored) {
+ }
}
+
+ final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
+ mCallback.onSessionFinished(this, success);
}
private void destroyInternal() {
@@ -578,9 +634,11 @@
pw.printPair("mClientProgress", mClientProgress);
pw.printPair("mProgress", mProgress);
pw.printPair("mSealed", mSealed);
- pw.printPair("mPermissionsConfirmed", mPermissionsConfirmed);
+ pw.printPair("mPermissionsAccepted", mPermissionsAccepted);
pw.printPair("mDestroyed", mDestroyed);
pw.printPair("mBridges", mBridges.size());
+ pw.printPair("mFinalStatus", mFinalStatus);
+ pw.printPair("mFinalMessage", mFinalMessage);
pw.println();
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4bf6636..6802fac 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4084,7 +4084,8 @@
}
for (File file : files) {
- final boolean isPackage = isApkFile(file) || file.isDirectory();
+ final boolean isPackage = (isApkFile(file) || file.isDirectory())
+ && !PackageInstallerService.isStageFile(file);
if (!isPackage) {
// Ignore entries which are not apk's
continue;
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 74f725f..71f43b4 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -37,13 +37,12 @@
import android.media.AudioPortConfig;
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
-import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvContract;
+import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
import android.media.tv.TvStreamConfig;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -59,12 +58,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* A helper class for TvInputManagerService to handle TV input hardware.
@@ -82,11 +79,11 @@
private final TvInputHal mHal = new TvInputHal(this);
private final SparseArray<Connection> mConnections = new SparseArray<>();
private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
- private List<HdmiDeviceInfo> mHdmiCecDeviceList = new LinkedList<>();
+ private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
/* A map from a device ID to the matching TV input ID. */
private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
/* A map from a HDMI logical address to the matching TV input ID. */
- private final SparseArray<String> mHdmiCecInputIdMap = new SparseArray<>();
+ private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
private final AudioManager mAudioManager;
@@ -119,7 +116,7 @@
try {
mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
- mHdmiCecDeviceList.addAll(mHdmiControlService.getInputDevices());
+ mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
mHdmiControlService.setInputChangeListener(mHdmiInputChangeListener);
} catch (RemoteException e) {
Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
@@ -163,12 +160,11 @@
buildHardwareListLocked();
TvInputHardwareInfo info = connection.getHardwareInfoLocked();
if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
- // Remove HDMI CEC devices linked with this hardware.
- for (Iterator<HdmiDeviceInfo> it = mHdmiCecDeviceList.iterator();
- it.hasNext(); ) {
+ // Remove HDMI devices linked with this hardware.
+ for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
HdmiDeviceInfo deviceInfo = it.next();
if (deviceInfo.getPortId() == info.getHdmiPortId()) {
- mHandler.obtainMessage(ListenerHandler.HDMI_CEC_DEVICE_REMOVED, 0, 0,
+ mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
deviceInfo).sendToTarget();
it.remove();
}
@@ -220,9 +216,9 @@
}
}
- public List<HdmiDeviceInfo> getHdmiCecInputDeviceList() {
+ public List<HdmiDeviceInfo> getHdmiDeviceList() {
synchronized (mLock) {
- return Collections.unmodifiableList(mHdmiCecDeviceList);
+ return Collections.unmodifiableList(mHdmiDeviceList);
}
}
@@ -283,7 +279,7 @@
return -1;
}
- public void addHdmiCecTvInput(int logicalAddress, TvInputInfo info) {
+ public void addHdmiTvInput(int logicalAddress, TvInputInfo info) {
if (info.getType() != TvInputInfo.TYPE_HDMI) {
throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
}
@@ -293,13 +289,13 @@
if (parentIndex < 0) {
throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
}
- String oldInputId = mHdmiCecInputIdMap.get(logicalAddress);
+ String oldInputId = mHdmiInputIdMap.get(logicalAddress);
if (oldInputId != null) {
Slog.w(TAG, "Trying to override previous registration: old = "
+ mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = "
+ info + ":" + logicalAddress);
}
- mHdmiCecInputIdMap.put(logicalAddress, info.getId());
+ mHdmiInputIdMap.put(logicalAddress, info.getId());
mInputMap.put(info.getId(), info);
}
}
@@ -311,9 +307,9 @@
if (hardwareIndex >= 0) {
mHardwareInputIdMap.removeAt(hardwareIndex);
}
- int cecIndex = indexOfEqualValue(mHdmiCecInputIdMap, inputId);
- if (cecIndex >= 0) {
- mHdmiCecInputIdMap.removeAt(cecIndex);
+ int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
+ if (deviceIndex >= 0) {
+ mHdmiInputIdMap.removeAt(deviceIndex);
}
}
}
@@ -864,16 +860,16 @@
public void onStateChanged(String inputId, int state);
public void onHardwareDeviceAdded(TvInputHardwareInfo info);
public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
- public void onHdmiCecDeviceAdded(HdmiDeviceInfo cecDevice);
- public void onHdmiCecDeviceRemoved(HdmiDeviceInfo cecDevice);
+ public void onHdmiDeviceAdded(HdmiDeviceInfo device);
+ public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
}
private class ListenerHandler extends Handler {
private static final int STATE_CHANGED = 1;
private static final int HARDWARE_DEVICE_ADDED = 2;
private static final int HARDWARE_DEVICE_REMOVED = 3;
- private static final int HDMI_CEC_DEVICE_ADDED = 4;
- private static final int HDMI_CEC_DEVICE_REMOVED = 5;
+ private static final int HDMI_DEVICE_ADDED = 4;
+ private static final int HDMI_DEVICE_REMOVED = 5;
@Override
public final void handleMessage(Message msg) {
@@ -894,14 +890,14 @@
mListener.onHardwareDeviceRemoved(info);
break;
}
- case HDMI_CEC_DEVICE_ADDED: {
+ case HDMI_DEVICE_ADDED: {
HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
- mListener.onHdmiCecDeviceAdded(info);
+ mListener.onHdmiDeviceAdded(info);
break;
}
- case HDMI_CEC_DEVICE_REMOVED: {
+ case HDMI_DEVICE_REMOVED: {
HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
- mListener.onHdmiCecDeviceRemoved(info);
+ mListener.onHdmiDeviceRemoved(info);
break;
}
default: {
@@ -939,21 +935,21 @@
public void onStatusChanged(HdmiDeviceInfo deviceInfo, boolean activated) {
synchronized (mLock) {
if (activated) {
- if (!mHdmiCecDeviceList.contains(deviceInfo)) {
- mHdmiCecDeviceList.add(deviceInfo);
+ if (!mHdmiDeviceList.contains(deviceInfo)) {
+ mHdmiDeviceList.add(deviceInfo);
} else {
Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
return;
}
} else {
- if (!mHdmiCecDeviceList.remove(deviceInfo)) {
+ if (!mHdmiDeviceList.remove(deviceInfo)) {
Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
return;
}
}
Message msg = mHandler.obtainMessage(
- activated ? ListenerHandler.HDMI_CEC_DEVICE_ADDED
- : ListenerHandler.HDMI_CEC_DEVICE_REMOVED,
+ activated ? ListenerHandler.HDMI_DEVICE_ADDED
+ : ListenerHandler.HDMI_DEVICE_REMOVED,
0, 0, deviceInfo);
if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
msg.sendToTarget();
@@ -970,7 +966,7 @@
String inputId;
synchronized (mLock) {
if (device.isCecDevice()) {
- inputId = mHdmiCecInputIdMap.get(device.getLogicalAddress());
+ inputId = mHdmiInputIdMap.get(device.getLogicalAddress());
} else {
TvInputHardwareInfo hardwareInfo =
findHardwareInfoForHdmiPortLocked(device.getPortId());
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 14d1ec4..c5e30d5 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1933,13 +1933,13 @@
}
}
- List<HdmiDeviceInfo> cecDeviceInfoList =
- mTvInputHardwareManager.getHdmiCecInputDeviceList();
- for (HdmiDeviceInfo cecDeviceInfo : cecDeviceInfoList) {
+ List<HdmiDeviceInfo> deviceInfoList =
+ mTvInputHardwareManager.getHdmiDeviceList();
+ for (HdmiDeviceInfo deviceInfo : deviceInfoList) {
try {
- serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo);
+ serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
} catch (RemoteException e) {
- Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e);
+ Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
}
}
}
@@ -2025,11 +2025,11 @@
}
@Override
- public void addHdmiCecTvInput(int logicalAddress, TvInputInfo inputInfo) {
+ public void addHdmiTvInput(int logicalAddress, TvInputInfo inputInfo) {
ensureHardwarePermission();
ensureValidInput(inputInfo);
synchronized (mLock) {
- mTvInputHardwareManager.addHdmiCecTvInput(logicalAddress, inputInfo);
+ mTvInputHardwareManager.addHdmiTvInput(logicalAddress, inputInfo);
addTvInputLocked(inputInfo);
}
}
@@ -2275,32 +2275,32 @@
}
@Override
- public void onHdmiCecDeviceAdded(HdmiDeviceInfo cecDeviceInfo) {
+ public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
synchronized (mLock) {
UserState userState = getUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
if (!serviceState.mIsHardware || serviceState.mService == null) continue;
try {
- serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo);
+ serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
} catch (RemoteException e) {
- Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e);
+ Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
}
}
}
}
@Override
- public void onHdmiCecDeviceRemoved(HdmiDeviceInfo cecDeviceInfo) {
+ public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
synchronized (mLock) {
UserState userState = getUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
if (!serviceState.mIsHardware || serviceState.mService == null) continue;
try {
- serviceState.mService.notifyHdmiCecDeviceRemoved(cecDeviceInfo);
+ serviceState.mService.notifyHdmiDeviceRemoved(deviceInfo);
} catch (RemoteException e) {
- Slog.e(TAG, "error in notifyHdmiCecDeviceRemoved", e);
+ Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
}
}
}
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index 3374d51..55cb8b1 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -69,6 +69,11 @@
*/
public static final int STATE_PRE_DIAL_WAIT = 8;
+ /**
+ * The state of an outgoing {@code Call}, before Telecomm broadcast intent has returned.
+ */
+ public static final int STATE_CONNECTING = 9;
+
public static class Details {
private final Uri mHandle;
private final int mHandlePresentation;
@@ -771,6 +776,8 @@
switch (parcelableCallState) {
case NEW:
return STATE_NEW;
+ case CONNECTING:
+ return STATE_CONNECTING;
case PRE_DIAL_WAIT:
return STATE_PRE_DIAL_WAIT;
case DIALING:
diff --git a/telecomm/java/android/telecomm/CallState.java b/telecomm/java/android/telecomm/CallState.java
index 9cb05db..cfa78d4 100644
--- a/telecomm/java/android/telecomm/CallState.java
+++ b/telecomm/java/android/telecomm/CallState.java
@@ -32,6 +32,12 @@
NEW,
/**
+ * Indicates an outgoing call has been initiated and is waiting for the broadcast intent to
+ * return and provide call details before proceeding.
+ */
+ CONNECTING,
+
+ /**
* Indicates that the call is about to go into the outgoing and dialing state but is waiting for
* user input before it proceeds. For example, where no default {@link PhoneAccount} is set,
* this is the state where the InCallUI is waiting for the user to select a
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
index 5b1f6ab..bbf1a17 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
@@ -28,8 +28,7 @@
l-5.046875,0.0 0.0-1.0Z" />
<path
android:name="two"
- android:fillColor="#ffff00"
- android:fillOpacity="0"
+ android:fillColor="#00ffff00"
android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0-5.5625,0.0 0.0-1.0q 0.671875-0.6875 1.828125-1.859375
q 1.1718752-1.1875 1.4687502-1.53125 0.578125-0.625 0.796875-1.0625
q 0.234375-0.453125 0.234375-0.875 0.0-0.703125-0.5-1.140625
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
index 8cabca8..1aad7430 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
@@ -21,10 +21,10 @@
<group>
<path
- android:fillOpacity="0.9"
+ android:fillColor="#E6000000"
android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" />
<path
- android:fillOpacity="0.9"
+ android:fillColor="#E6000000"
android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" />
</group>
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 4f1d15e..ec284c5 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1739,7 +1739,7 @@
static status_t writeLayoutClasses(
FILE* fp, const sp<AaptAssets>& assets,
- const sp<AaptSymbols>& symbols, int indent, bool includePrivate)
+ const sp<AaptSymbols>& symbols, int indent, bool includePrivate, bool nonConstantId)
{
const char* indentStr = getIndentSpace(indent);
if (!includePrivate) {
@@ -1957,8 +1957,13 @@
getSymbolName(name8).string());
fprintf(fp, "%s*/\n", indentStr);
ann.printAnnotations(fp, indentStr);
+
+ const char * id_format = nonConstantId ?
+ "%spublic static int %s_%s = %d;\n" :
+ "%spublic static final int %s_%s = %d;\n";
+
fprintf(fp,
- "%spublic static final int %s_%s = %d;\n",
+ id_format,
indentStr, nclassName.string(),
flattenSymbol(name8).string(), (int)pos);
}
@@ -2177,7 +2182,7 @@
}
if (styleableSymbols != NULL) {
- err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate);
+ err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate, nonConstantId);
if (err != NO_ERROR) {
return err;
}
diff --git a/tools/layoutlib/rename_font/build_font_single.py b/tools/layoutlib/rename_font/build_font_single.py
new file mode 100755
index 0000000..d648b04
--- /dev/null
+++ b/tools/layoutlib/rename_font/build_font_single.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python
+
+# 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.
+
+"""
+Rename the PS name of the input font.
+
+OpenType fonts (*.otf) are not currently supported. They are copied to the destination without renaming.
+XML files are also copied in case they are passed there by mistake.
+
+Usage: build_font.py /path/to/input_font.ttf /path/to/output_font.ttf
+
+"""
+
+import glob
+import os
+import re
+import shutil
+import sys
+import xml.etree.ElementTree as etree
+
+# Prevent .pyc files from being created.
+sys.dont_write_bytecode = True
+
+# fontTools is available at platform/external/fonttools
+from fontTools import ttx
+
+
+class FontInfo(object):
+ family = None
+ style = None
+ version = None
+ ends_in_regular = False
+ fullname = None
+
+
+class InvalidFontException(Exception):
+ pass
+
+
+# A constant to copy the font without modifying. This is useful when running
+# locally and speed up the time to build the SDK.
+COPY_ONLY = False
+
+# These constants represent the value of nameID parameter in the namerecord for
+# different information.
+# see http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b
+NAMEID_FAMILY = 1
+NAMEID_STYLE = 2
+NAMEID_FULLNAME = 4
+NAMEID_VERSION = 5
+
+# A list of extensions to process.
+EXTENSIONS = ['.ttf', '.otf', '.xml']
+
+def main(argv):
+ if len(argv) < 2:
+ sys.exit('Usage: build_font.py /path/to/input/font.ttf /path/to/out/font.ttf')
+ dest_path = argv[-1]
+ input_path = argv[0]
+ extension = os.path.splitext(input_path)[1].lower()
+ if extension in EXTENSIONS:
+ if not COPY_ONLY and extension == '.ttf':
+ convert_font(input_path, dest_path)
+ return
+ shutil.copy(input_path, dest_path)
+
+
+def convert_font(input_path, dest_path):
+ filename = os.path.basename(input_path)
+ print 'Converting font: ' + filename
+ # the path to the output file. The file name is the fontfilename.ttx
+ ttx_path = dest_path[:-1] + 'x'
+ try:
+ # run ttx to generate an xml file in the output folder which represents all
+ # its info
+ ttx_args = ['-q', '-o', ttx_path, input_path]
+ ttx.main(ttx_args)
+ # now parse the xml file to change its PS name.
+ tree = etree.parse(ttx_path)
+ root = tree.getroot()
+ for name in root.iter('name'):
+ update_tag(name, get_font_info(name))
+ tree.write(ttx_path, xml_declaration=True, encoding='utf-8')
+ # generate the udpated font now.
+ ttx_args = ['-q', '-o', dest_path, ttx_path]
+ ttx.main(ttx_args)
+ except InvalidFontException:
+ # In case of invalid fonts, we exit.
+ print filename + ' is not a valid font'
+ raise
+ except Exception as e:
+ print 'Error converting font: ' + filename
+ print e
+ # Some fonts are too big to be handled by the ttx library.
+ # Just copy paste them.
+ shutil.copy(input_path, dest_path)
+ try:
+ # delete the temp ttx file is it exists.
+ os.remove(ttx_path)
+ except OSError:
+ pass
+
+
+def get_font_info(tag):
+ """ Returns a list of FontInfo representing the various sets of namerecords
+ found in the name table of the font. """
+ fonts = []
+ font = None
+ last_name_id = sys.maxint
+ for namerecord in tag.iter('namerecord'):
+ if 'nameID' in namerecord.attrib:
+ name_id = int(namerecord.attrib['nameID'])
+ # A new font should be created for each platform, encoding and language
+ # id. But, since the nameIDs are sorted, we use the easy approach of
+ # creating a new one when the nameIDs reset.
+ if name_id <= last_name_id and font is not None:
+ fonts.append(font)
+ font = None
+ last_name_id = name_id
+ if font is None:
+ font = FontInfo()
+ if name_id == NAMEID_FAMILY:
+ font.family = namerecord.text.strip()
+ if name_id == NAMEID_STYLE:
+ font.style = namerecord.text.strip()
+ if name_id == NAMEID_FULLNAME:
+ font.ends_in_regular = ends_in_regular(namerecord.text)
+ font.fullname = namerecord.text.strip()
+ if name_id == NAMEID_VERSION:
+ font.version = get_version(namerecord.text)
+ if font is not None:
+ fonts.append(font)
+ return fonts
+
+
+def update_tag(tag, fonts):
+ last_name_id = sys.maxint
+ fonts_iterator = fonts.__iter__()
+ font = None
+ for namerecord in tag.iter('namerecord'):
+ if 'nameID' in namerecord.attrib:
+ name_id = int(namerecord.attrib['nameID'])
+ if name_id <= last_name_id:
+ font = fonts_iterator.next()
+ font = update_font_name(font)
+ last_name_id = name_id
+ if name_id == NAMEID_FAMILY:
+ namerecord.text = font.family
+ if name_id == NAMEID_FULLNAME:
+ namerecord.text = font.fullname
+
+
+def update_font_name(font):
+ """ Compute the new font family name and font fullname. If the font has a
+ valid version, it's sanitized and appended to the font family name. The
+ font fullname is then created by joining the new family name and the
+ style. If the style is 'Regular', it is appended only if the original font
+ had it. """
+ if font.family is None or font.style is None:
+ raise InvalidFontException('Font doesn\'t have proper family name or style')
+ if font.version is not None:
+ new_family = font.family + font.version
+ else:
+ new_family = font.family
+ if font.style is 'Regular' and not font.ends_in_regular:
+ font.fullname = new_family
+ else:
+ font.fullname = new_family + ' ' + font.style
+ font.family = new_family
+ return font
+
+
+def ends_in_regular(string):
+ """ According to the specification, the font fullname should not end in
+ 'Regular' for plain fonts. However, some fonts don't obey this rule. We
+ keep the style info, to minimize the diff. """
+ string = string.strip().split()[-1]
+ return string is 'Regular'
+
+
+def get_version(string):
+ # The string must begin with 'Version n.nn '
+ # to extract n.nn, we return the second entry in the split strings.
+ string = string.strip()
+ if not string.startswith('Version '):
+ raise InvalidFontException('mal-formed font version')
+ return sanitize(string.split()[1])
+
+
+def sanitize(string):
+ return re.sub(r'[^\w-]+', '', string)
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index c5c44b5..e7bcb23 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -100,7 +100,7 @@
*/
public static class ChannelSpec {
/**
- * channel frequency in KHz; for example channel 1 is specified as 2412
+ * channel frequency in MHz; for example channel 1 is specified as 2412
*/
public int frequency;
/**
@@ -158,6 +158,7 @@
dest.writeInt(band);
dest.writeInt(periodInMs);
dest.writeInt(reportEvents);
+ dest.writeInt(numBssidsPerScan);
if (channels != null) {
dest.writeInt(channels.length);
@@ -181,6 +182,7 @@
settings.band = in.readInt();
settings.periodInMs = in.readInt();
settings.reportEvents = in.readInt();
+ settings.numBssidsPerScan = in.readInt();
int num_channels = in.readInt();
settings.channels = new ChannelSpec[num_channels];
for (int i = 0; i < num_channels; i++) {