Merge "SurfaceTexture: Allow creation in detached mode"
diff --git a/api/current.txt b/api/current.txt
index fbbae6c..1f1fd65 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12517,6 +12517,7 @@
field public static final int CONTROL_SCENE_MODE_DISABLED = 0; // 0x0
field public static final int CONTROL_SCENE_MODE_FACE_PRIORITY = 1; // 0x1
field public static final int CONTROL_SCENE_MODE_FIREWORKS = 12; // 0xc
+ field public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
field public static final int CONTROL_SCENE_MODE_LANDSCAPE = 4; // 0x4
field public static final int CONTROL_SCENE_MODE_NIGHT = 5; // 0x5
field public static final int CONTROL_SCENE_MODE_NIGHT_PORTRAIT = 6; // 0x6
@@ -27662,14 +27663,14 @@
public abstract class Connection {
ctor protected Connection();
- method public final void conference();
method public final android.telecomm.CallAudioState getCallAudioState();
- method public java.util.List<android.telecomm.Connection> getChildConnections();
+ method public final java.util.List<android.telecomm.Connection> getChildConnections();
method public final android.net.Uri getHandle();
- method public android.telecomm.Connection getParentConnection();
- method public boolean isConferenceCapable();
- method public boolean isConferenceConnection();
- method public boolean isRequestingRingback();
+ method public final android.telecomm.Connection getParentConnection();
+ method public final int getState();
+ method public final boolean isConferenceCapable();
+ method public final boolean isConferenceConnection();
+ method public final boolean isRequestingRingback();
method protected void onAbort();
method protected void onAnswer();
method protected void onChildrenChanged(java.util.List<android.telecomm.Connection>);
@@ -27681,21 +27682,20 @@
method protected void onReject();
method protected void onSeparate();
method protected void onSetAudioState(android.telecomm.CallAudioState);
- method protected void onSetSignal(android.os.Bundle);
method protected void onSetState(int);
method protected void onStopDtmfTone();
method protected void onUnhold();
- method protected void setActive();
- method public void setAudioState(android.telecomm.CallAudioState);
- method protected void setDestroyed();
- method protected void setDialing();
- method protected void setDisconnected(int, java.lang.String);
- method protected void setHandle(android.net.Uri);
- method protected void setIsConferenceCapable(boolean);
- method protected void setOnHold();
- method public void setParentConnection(android.telecomm.Connection);
- method protected void setRequestingRingback(boolean);
- method protected void setRinging();
+ method public final void setActive();
+ method public final void setDestroyed();
+ method public final void setDialing();
+ method public final void setDisconnected(int, java.lang.String);
+ method public final void setHandle(android.net.Uri);
+ method public final void setIsConferenceCapable(boolean);
+ method public final void setOnHold();
+ method public final void setParentConnection(android.telecomm.Connection);
+ method public final void setRequestingRingback(boolean);
+ method public final void setRinging();
+ method public final void setSignal(android.os.Bundle);
method public static java.lang.String stateToString(int);
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 88746bf4..87140a3 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -538,6 +538,30 @@
}
/**
+ * Sets the label for this task description.
+ * @hide
+ */
+ public void setLabel(String label) {
+ mLabel = label;
+ }
+
+ /**
+ * Sets the primary color for this task description.
+ * @hide
+ */
+ public void setPrimaryColor(int primaryColor) {
+ mColorPrimary = primaryColor;
+ }
+
+ /**
+ * Sets the icon for this task description.
+ * @hide
+ */
+ public void setIcon(Bitmap icon) {
+ mIcon = icon;
+ }
+
+ /**
* @return The label and description of the current state of this task.
*/
public String getLabel() {
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index ef8c67b..14e6f92 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -466,6 +466,34 @@
new Key<Integer>("android.control.maxRegionsAf", int.class);
/**
+ * <p>List of available high speed video size and fps range configurations
+ * supported by the camera device, in the format of (width, height, fps_min, fps_max).</p>
+ * <p>When HIGH_SPEED_VIDEO is supported in {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes},
+ * this metadata will list the supported high speed video size and fps range
+ * configurations. All the sizes listed in this configuration will be a subset
+ * of the sizes reported by StreamConfigurationMap#getOutputSizes for processed
+ * non-stalling formats.</p>
+ * <p>For the high speed video use case, where the application will set
+ * {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} to HIGH_SPEED_VIDEO in capture requests, the application must
+ * select the video size and fps range from this metadata to configure the recording and
+ * preview streams and setup the recording requests. For example, if the application intends
+ * to do high speed recording, it can select the maximum size reported by this metadata to
+ * configure output streams. Once the size is selected, application can filter this metadata
+ * by selected size and get the supported fps ranges, and use these fps ranges to setup the
+ * recording requests.</p>
+ * <p>For normal video recording use case, where some application will NOT set
+ * {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} to HIGH_SPEED_VIDEO in capture requests, the fps ranges
+ * reported in this metadata must not be used to setup capture requests, or it will cause
+ * request error.</p>
+ *
+ * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES
+ * @see CaptureRequest#CONTROL_SCENE_MODE
+ * @hide
+ */
+ public static final Key<int[]> CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS =
+ new Key<int[]>("android.control.availableHighSpeedVideoConfigurations", int[].class);
+
+ /**
* <p>The set of edge enhancement modes supported by this camera device.</p>
* <p>This tag lists the valid modes for {@link CaptureRequest#EDGE_MODE android.edge.mode}.</p>
* <p>Full-capability camera devices must always support OFF and FAST.</p>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 889b127..e464f2a 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1393,6 +1393,84 @@
*/
public static final int CONTROL_SCENE_MODE_BARCODE = 16;
+ /**
+ * <p>Optimized for high speed video recording (frame rate >=60fps) use case.</p>
+ * <p>The supported high speed video sizes and fps ranges are specified in
+ * android.control.availableHighSpeedVideoConfigurations. To get desired
+ * output frame rates, the application is only allowed to select video size
+ * and fps range combinations listed in this static metadata. The fps range
+ * can be control via {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}.</p>
+ * <p>In this mode, the camera device will override aeMode, awbMode, and afMode to
+ * ON, ON, and CONTINUOUS_VIDEO, respectively. All post-processing block mode
+ * controls will be overridden to be FAST. Therefore, no manual control of capture
+ * and post-processing parameters is possible. All other controls operate the
+ * same as when {@link CaptureRequest#CONTROL_MODE android.control.mode} == AUTO. This means that all other
+ * android.control.* fields continue to work, such as</p>
+ * <ul>
+ * <li>{@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock}</li>
+ * <li>{@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock}</li>
+ * <li>{@link CaptureRequest#CONTROL_EFFECT_MODE android.control.effectMode}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}</li>
+ * </ul>
+ * <p>Outside of android.control.*, the following controls will work:</p>
+ * <ul>
+ * <li>{@link CaptureRequest#FLASH_MODE android.flash.mode} (automatic flash for still capture will not work since aeMode is ON)</li>
+ * <li>{@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode} (if it is supported)</li>
+ * <li>{@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}</li>
+ * <li>{@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode}</li>
+ * </ul>
+ * <p>For high speed recording use case, the actual maximum supported frame rate may
+ * be lower than what camera can output, depending on the destination Surfaces for
+ * the image data. For example, if the destination surface is from video encoder,
+ * the application need check if the video encoder is capable of supporting the
+ * high frame rate for a given video size, or it will end up with lower recording
+ * frame rate. If the destination surface is from preview window, the preview frame
+ * rate will be bounded by the screen refresh rate.</p>
+ * <p>The camera device will only support up to 2 output high speed streams
+ * (processed non-stalling format defined in android.request.maxNumOutputStreams)
+ * in this mode. This control will be effective only if all of below conditions are true:</p>
+ * <ul>
+ * <li>The application created no more than maxNumHighSpeedStreams processed non-stalling
+ * format output streams, where maxNumHighSpeedStreams is calculated as
+ * min(2, android.request.maxNumOutputStreams[Processed (but not-stalling)]).</li>
+ * <li>The stream sizes are selected from the sizes reported by
+ * android.control.availableHighSpeedVideoConfigurations.</li>
+ * <li>No processed non-stalling or raw streams are configured.</li>
+ * </ul>
+ * <p>When above conditions are NOT satistied, the controls of this mode and
+ * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange} will be ignored by the camera device,
+ * the camera device will fall back to {@link CaptureRequest#CONTROL_MODE android.control.mode} <code>==</code> AUTO,
+ * and the returned capture result metadata will give the fps range choosen
+ * by the camera device.</p>
+ * <p>Switching into or out of this mode may trigger some camera ISP/sensor
+ * reconfigurations, which may introduce extra latency. It is recommended that
+ * the application avoids unnecessary scene mode switch as much as possible.</p>
+ *
+ * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION
+ * @see CaptureRequest#CONTROL_AE_LOCK
+ * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+ * @see CaptureRequest#CONTROL_AE_REGIONS
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+ * @see CaptureRequest#CONTROL_AF_REGIONS
+ * @see CaptureRequest#CONTROL_AF_TRIGGER
+ * @see CaptureRequest#CONTROL_AWB_LOCK
+ * @see CaptureRequest#CONTROL_AWB_REGIONS
+ * @see CaptureRequest#CONTROL_EFFECT_MODE
+ * @see CaptureRequest#CONTROL_MODE
+ * @see CaptureRequest#FLASH_MODE
+ * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
+ * @see CaptureRequest#SCALER_CROP_REGION
+ * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
+ * @see CaptureRequest#CONTROL_SCENE_MODE
+ */
+ public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17;
+
//
// Enumeration values for CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
//
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 5bc59dc..91ff7fa 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1011,6 +1011,7 @@
* @see #CONTROL_SCENE_MODE_PARTY
* @see #CONTROL_SCENE_MODE_CANDLELIGHT
* @see #CONTROL_SCENE_MODE_BARCODE
+ * @see #CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO
*/
public static final Key<Integer> CONTROL_SCENE_MODE =
new Key<Integer>("android.control.sceneMode", int.class);
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 9097220..be2d960 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -1581,6 +1581,7 @@
* @see #CONTROL_SCENE_MODE_PARTY
* @see #CONTROL_SCENE_MODE_CANDLELIGHT
* @see #CONTROL_SCENE_MODE_BARCODE
+ * @see #CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO
*/
public static final Key<Integer> CONTROL_SCENE_MODE =
new Key<Integer>("android.control.sceneMode", int.class);
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index d86dd5e..c87b674 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -101,6 +101,9 @@
/** Logical address used to indicate it is not initialized or invalid. */
public static final int ADDR_INVALID = -1;
+ /** Logical address used to indicate the source comes from internal device. */
+ public static final int ADDR_INTERNAL = 0xFFFF;
+
// TODO: Complete the list of CEC messages definition.
public static final int MESSAGE_FEATURE_ABORT = 0x00;
public static final int MESSAGE_IMAGE_VIEW_ON = 0x04;
diff --git a/docs/html/preview/index.html b/docs/html/preview/index.html
index 4f3f150..51597fe 100644
--- a/docs/html/preview/index.html
+++ b/docs/html/preview/index.html
@@ -257,7 +257,7 @@
Join the community of Android developers testing out the L Developer Preview and
share your thoughts and experiences.
</p><p class="landing-small">
- <a href="https://plus.google.com/communities/113159138894928487684">
+ <a href="https://plus.google.com/communities/101985907812750684586">
Discuss on Google+</a>
</p>
</div>
diff --git a/docs/html/preview/support.jd b/docs/html/preview/support.jd
index 8efc4bc..9d7844b 100644
--- a/docs/html/preview/support.jd
+++ b/docs/html/preview/support.jd
@@ -7,7 +7,7 @@
our issue tracker.</p>
<p>For more support,
-<a href="https://plus.google.com/communities/113159138894928487684">join
+<a href="https://plus.google.com/communities/101985907812750684586">join
the L Developer Preview Google+ community</a> to discuss your development experiences.
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 0825f2e..f4e9876 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -79,8 +79,6 @@
private static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package";
private static final String ATTR_RESOLVEDTYPE = "resolved_type";
private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
- private static final String ATTR_TASKDESCRIPTIONLABEL = "task_description_label";
- private static final String ATTR_TASKDESCRIPTIONCOLOR = "task_description_color";
private static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
final ActivityManagerService service; // owner
@@ -1064,20 +1062,10 @@
}
out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified));
out.attribute(null, ATTR_USERID, String.valueOf(userId));
+
if (taskDescription != null) {
- final String label = taskDescription.getLabel();
- if (label != null) {
- out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, label);
- }
- final int colorPrimary = taskDescription.getPrimaryColor();
- if (colorPrimary != 0) {
- out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(colorPrimary));
- }
- final Bitmap icon = taskDescription.getIcon();
- if (icon != null) {
- TaskPersister.saveImage(icon, String.valueOf(task.taskId) + ACTIVITY_ICON_SUFFIX +
- createTime);
- }
+ TaskPersister.saveTaskDescription(taskDescription, String.valueOf(task.taskId) +
+ ACTIVITY_ICON_SUFFIX + createTime, out);
}
out.startTag(null, TAG_INTENT);
@@ -1100,10 +1088,9 @@
String resolvedType = null;
boolean componentSpecified = false;
int userId = 0;
- String activityLabel = null;
- int activityColor = 0;
long createTime = -1;
final int outerDepth = in.getDepth();
+ TaskDescription taskDescription = new TaskDescription();
for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
final String attrName = in.getAttributeName(attrNdx);
@@ -1122,10 +1109,9 @@
componentSpecified = Boolean.valueOf(attrValue);
} else if (ATTR_USERID.equals(attrName)) {
userId = Integer.valueOf(attrValue);
- } else if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) {
- activityLabel = attrValue;
- } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) {
- activityColor = (int) Long.parseLong(attrValue, 16);
+ } else if (TaskPersister.readTaskDescriptionAttribute(taskDescription, attrName,
+ attrValue)) {
+ // Completed in TaskPersister.readTaskDescriptionAttribute()
} else {
Log.d(TAG, "Unknown ActivityRecord attribute=" + attrName);
}
@@ -1169,12 +1155,11 @@
r.persistentState = persistentState;
- Bitmap icon = null;
if (createTime >= 0) {
- icon = TaskPersister.restoreImage(String.valueOf(taskId) + ACTIVITY_ICON_SUFFIX +
- createTime);
+ taskDescription.setIcon(TaskPersister.restoreImage(String.valueOf(taskId) +
+ ACTIVITY_ICON_SUFFIX + createTime));
}
- r.taskDescription = new TaskDescription(activityLabel, icon, activityColor);
+ r.taskDescription = taskDescription;
r.createTime = createTime;
return r;
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index c79b33d..132b244 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.app.ActivityManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Debug;
@@ -56,6 +57,9 @@
private static final String TAG_TASK = "task";
+ private static final String ATTR_TASKDESCRIPTIONLABEL = "task_description_label";
+ private static final String ATTR_TASKDESCRIPTIONCOLOR = "task_description_color";
+
private static File sImagesDir;
private static File sTasksDir;
@@ -143,6 +147,36 @@
}
}
+ static void saveTaskDescription(ActivityManager.TaskDescription taskDescription,
+ String iconFilename, XmlSerializer out) throws IOException {
+ if (taskDescription != null) {
+ final String label = taskDescription.getLabel();
+ if (label != null) {
+ out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, label);
+ }
+ final int colorPrimary = taskDescription.getPrimaryColor();
+ if (colorPrimary != 0) {
+ out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(colorPrimary));
+ }
+ final Bitmap icon = taskDescription.getIcon();
+ if (icon != null) {
+ saveImage(icon, iconFilename);
+ }
+ }
+ }
+
+ static boolean readTaskDescriptionAttribute(ActivityManager.TaskDescription taskDescription,
+ String attrName, String attrValue) {
+ if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) {
+ taskDescription.setLabel(attrValue);
+ return true;
+ } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) {
+ taskDescription.setPrimaryColor((int) Long.parseLong(attrValue, 16));
+ return true;
+ }
+ return false;
+ }
+
ArrayList<TaskRecord> restoreTasksLocked() {
final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 57dee2e..0bd7ef9 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -60,6 +60,7 @@
private static final String ATTR_LASTDESCRIPTION = "last_description";
private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
+ private static final String LAST_ACTIVITY_ICON_SUFFIX = "_last_activity_icon_";
private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
@@ -138,7 +139,8 @@
String _affinity, ComponentName _realActivity, ComponentName _origActivity,
boolean _rootWasReset, boolean _askedCompatMode, int _taskType, int _userId,
String _lastDescription, ArrayList<ActivityRecord> activities, long _lastActiveTime,
- long lastTimeMoved, boolean neverRelinquishIdentity) {
+ long lastTimeMoved, boolean neverRelinquishIdentity,
+ ActivityManager.TaskDescription _lastTaskDescription) {
mService = service;
taskId = _taskId;
intent = _intent;
@@ -158,8 +160,7 @@
mActivities = activities;
mLastTimeMoved = lastTimeMoved;
mNeverRelinquishIdentity = neverRelinquishIdentity;
- // Recompute the task description for this task
- updateTaskDescription();
+ lastTaskDescription = _lastTaskDescription;
}
void touchActiveTime() {
@@ -714,6 +715,11 @@
out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
}
+ if (lastTaskDescription != null) {
+ TaskPersister.saveTaskDescription(lastTaskDescription, String.valueOf(taskId) +
+ LAST_ACTIVITY_ICON_SUFFIX + lastActiveTime, out);
+ }
+
if (affinityIntent != null) {
out.startTag(null, TAG_AFFINITYINTENT);
affinityIntent.saveToXml(out);
@@ -758,11 +764,12 @@
int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
int userId = 0;
String lastDescription = null;
- long lastActiveTime = 0;
+ long lastActiveTime = -1;
long lastTimeOnTop = 0;
boolean neverRelinquishIdentity = true;
int taskId = -1;
final int outerDepth = in.getDepth();
+ ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription();
for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
final String attrName = in.getAttributeName(attrNdx);
@@ -793,6 +800,9 @@
lastTimeOnTop = Long.valueOf(attrValue);
} else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
neverRelinquishIdentity = Boolean.valueOf(attrValue);
+ } else if (TaskPersister.readTaskDescriptionAttribute(taskDescription, attrName,
+ attrValue)) {
+ // Completed in TaskPersister.readTaskDescriptionAttribute()
} else {
Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
}
@@ -824,10 +834,15 @@
}
}
+ if (lastActiveTime >= 0) {
+ taskDescription.setIcon(TaskPersister.restoreImage(String.valueOf(taskId) +
+ LAST_ACTIVITY_ICON_SUFFIX + lastActiveTime));
+ }
+
final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
affinityIntent, affinity, realActivity, origActivity, rootHasReset,
askedCompatMode, taskType, userId, lastDescription, activities, lastActiveTime,
- lastTimeOnTop, neverRelinquishIdentity);
+ lastTimeOnTop, neverRelinquishIdentity, taskDescription);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 74eaf2a..8de6763 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -27,18 +27,18 @@
/**
* Handles CEC command <Active Source>.
* <p>
- * Used by feature actions that need to handle the command in their flow.
+ * Used by feature actions that need to handle the command in their flow. Only for TV
+ * local device.
*/
final class ActiveSourceHandler {
private static final String TAG = "ActiveSourceHandler";
- private final HdmiCecLocalDevice mSource;
+ private final HdmiCecLocalDeviceTv mSource;
private final HdmiControlService mService;
@Nullable
private final IHdmiControlCallback mCallback;
- static ActiveSourceHandler create(HdmiCecLocalDevice source,
- IHdmiControlCallback callback) {
+ static ActiveSourceHandler create(HdmiCecLocalDeviceTv source, IHdmiControlCallback callback) {
if (source == null) {
Slog.e(TAG, "Wrong arguments");
return null;
@@ -46,7 +46,7 @@
return new ActiveSourceHandler(source, callback);
}
- private ActiveSourceHandler(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
+ private ActiveSourceHandler(HdmiCecLocalDeviceTv source, IHdmiControlCallback callback) {
mSource = source;
mService = mSource.getService();
mCallback = callback;
@@ -55,48 +55,46 @@
/**
* Handles the incoming active source command.
*
- * @param deviceLogicalAddress logical address of the device to be the active source
- * @param routingPath routing path of the device to be the active source
+ * @param activeAddress logical address of the device to be the active source
+ * @param activePath routing path of the device to be the active source
*/
- void process(int deviceLogicalAddress, int routingPath) {
- if (getSourcePath() == routingPath && mSource.getActiveSource() == getSourceAddress()) {
+ void process(int activeAddress, int activePath) {
+ // Seq #17
+ HdmiCecLocalDeviceTv tv = mSource;
+ if (getSourcePath() == activePath && tv.getActiveSource() == getSourceAddress()) {
invokeCallback(HdmiCec.RESULT_SUCCESS);
return;
}
- HdmiCecDeviceInfo device = mService.getDeviceInfo(deviceLogicalAddress);
+ HdmiCecDeviceInfo device = mService.getDeviceInfo(activeAddress);
if (device == null) {
// "New device action" initiated by <Active Source> does not require
// "Routing change action".
- mSource.addAndStartAction(new NewDeviceAction(mSource, deviceLogicalAddress,
- routingPath, false));
+ tv.addAndStartAction(new NewDeviceAction(tv, activeAddress, activePath, false));
}
- if (!mSource.isInPresetInstallationMode()) {
- int prevActiveInput = mSource.getActivePortId();
- mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
- if (prevActiveInput != mSource.getActivePortId()) {
- // TODO: change port input here.
+ int currentActive = tv.getActiveSource();
+ int currentPath = tv.getActivePath();
+ if (!tv.isInPresetInstallationMode()) {
+ tv.updateActiveSource(activeAddress, activePath);
+ if (currentActive != activeAddress && currentPath != activePath) {
+ tv.updateActivePortId(mService.pathToPortId(activePath));
}
invokeCallback(HdmiCec.RESULT_SUCCESS);
} else {
// TV is in a mode that should keep its current source/input from
// being changed for its operation. Reclaim the active source
// or switch the port back to the one used for the current mode.
- if (mSource.getActiveSource() == getSourceAddress()) {
+ if (currentActive == getSourceAddress()) {
HdmiCecMessage activeSource =
- HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(),
- getSourcePath());
+ HdmiCecMessageBuilder.buildActiveSource(currentActive, currentPath);
mService.sendCecCommand(activeSource);
- mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
+ tv.updateActiveSource(currentActive, currentPath);
invokeCallback(HdmiCec.RESULT_SUCCESS);
} else {
- int activePath = mSource.getActivePath();
- mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
- routingPath, activePath));
- // TODO: Start port select action here
- // PortSelectAction action = new PortSelectAction(mService, getSourceAddress(),
- // activePath, mCallback);
- // mService.addActionAndStart(action);
+ HdmiCecMessage routingChange = HdmiCecMessageBuilder.buildRoutingChange(
+ getSourceAddress(), activePath, currentPath);
+ mService.sendCecCommand(routingChange);
+ tv.addAndStartAction(new RoutingControlAction(tv, currentPath, mCallback));
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
index dbe3b80..fd3341a 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -77,7 +77,7 @@
* @param target target logical device that will be a new active source
* @param callback callback object
*/
- public DeviceSelectAction(HdmiCecLocalDevice source,
+ public DeviceSelectAction(HdmiCecLocalDeviceTv source,
HdmiCecDeviceInfo target, IHdmiControlCallback callback) {
super(source);
mCallback = callback;
@@ -116,7 +116,7 @@
if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) {
int activePath = HdmiUtils.twoBytesToInt(params);
ActiveSourceHandler
- .create(localDevice(), mCallback)
+ .create((HdmiCecLocalDeviceTv) localDevice(), mCallback)
.process(cmd.getSource(), activePath);
finish();
return true;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 6f7f5c2..f72d3f0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -125,6 +125,12 @@
return true;
}
switch (message.getOpcode()) {
+ case HdmiCec.MESSAGE_ACTIVE_SOURCE:
+ return handleActiveSource(message);
+ case HdmiCec.MESSAGE_INACTIVE_SOURCE:
+ return handleInactiveSource(message);
+ case HdmiCec.MESSAGE_REQUEST_ACTIVE_SOURCE:
+ return handleRequestActiveSource(message);
case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
return handleGetMenuLanguage(message);
case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS:
@@ -193,6 +199,21 @@
}
@ServiceThreadOnly
+ protected boolean handleActiveSource(HdmiCecMessage message) {
+ return false;
+ }
+
+ @ServiceThreadOnly
+ protected boolean handleInactiveSource(HdmiCecMessage message) {
+ return false;
+ }
+
+ @ServiceThreadOnly
+ protected boolean handleRequestActiveSource(HdmiCecMessage message) {
+ return false;
+ }
+
+ @ServiceThreadOnly
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
assertRunOnServiceThread();
Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
@@ -383,15 +404,24 @@
}
}
- /**
- * Returns the active routing path.
- */
+ void setActiveSource(int source) {
+ synchronized (mLock) {
+ mActiveSource = source;
+ }
+ }
+
int getActivePath() {
synchronized (mLock) {
return mActiveRoutingPath;
}
}
+ void setActivePath(int path) {
+ synchronized (mLock) {
+ mActiveRoutingPath = path;
+ }
+ }
+
/**
* Returns the ID of the active HDMI port. The active port is the one that has the active
* routing path connected to it directly or indirectly under the device hierarchy.
@@ -429,6 +459,13 @@
}
boolean isInPresetInstallationMode() {
+ // TODO: Change this to check the right flag.
+ synchronized (mLock) {
+ return !mInputChangeEnabled;
+ }
+ }
+
+ boolean isHdmiControlEnabled() {
synchronized (mLock) {
return !mInputChangeEnabled;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 0333dbf..2431ec4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -48,6 +48,14 @@
@GuardedBy("mLock")
private boolean mSystemAudioMode;
+ // The previous port id (input) before switching to the new one. This is remembered in order to
+ // be able to switch to it upon receiving <Inactive Source> from currently active source.
+ // This remains valid only when the active source was switched via one touch play operation
+ // (either by TV or source device). Manual port switching invalidates this value to
+ // HdmiConstants.PORT_INVALID, for which case <Inactive Source> does not do anything.
+ @GuardedBy("mLock")
+ private int mPrevPortId;
+
// Copy of mDeviceInfos to guarantee thread-safety.
@GuardedBy("mLock")
private List<HdmiCecDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
@@ -62,7 +70,7 @@
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiCec.DEVICE_TV);
-
+ mPrevPortId = HdmiConstants.INVALID_PORT_ID;
// TODO: load system audio mode and set it to mSystemAudioMode.
}
@@ -90,6 +98,10 @@
@ServiceThreadOnly
void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
assertRunOnServiceThread();
+ if (targetAddress == HdmiCec.ADDR_INTERNAL) {
+ handleSelectInternalSource(callback);
+ return;
+ }
HdmiCecDeviceInfo targetDevice = getDeviceInfo(targetAddress);
if (targetDevice == null) {
invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE);
@@ -99,30 +111,85 @@
addAndStartAction(new DeviceSelectAction(this, targetDevice, callback));
}
- /**
- * Performs the action routing control.
- *
- * @param portId new HDMI port to route to
- * @param callback callback object to report the result with
- */
@ServiceThreadOnly
- void portSelect(int portId, IHdmiControlCallback callback) {
+ private void handleSelectInternalSource(IHdmiControlCallback callback) {
assertRunOnServiceThread();
- if (isInPresetInstallationMode()) {
+ // Seq #18
+ if (isHdmiControlEnabled() && getActiveSource() != mAddress) {
+ updateActiveSource(mAddress, mService.getPhysicalAddress());
+ // TODO: Check if this comes from <Text/Image View On> - if true, do nothing.
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ mAddress, mService.getPhysicalAddress());
+ mService.sendCecCommand(activeSource);
+ }
+ }
+
+ @ServiceThreadOnly
+ void updateActiveSource(int activeSource, int activePath) {
+ assertRunOnServiceThread();
+ // Seq #14
+ if (activeSource == getActiveSource() && activePath == getActivePath()) {
+ return;
+ }
+ setActiveSource(activeSource);
+ setActivePath(activePath);
+ if (getDeviceInfo(activeSource) != null && activeSource != mAddress) {
+ if (mService.pathToPortId(activePath) == getActivePortId()) {
+ setPrevPortId(getActivePortId());
+ }
+ // TODO: Show the OSD banner related to the new active source device.
+ } else {
+ // TODO: If displayed, remove the OSD banner related to the previous
+ // active source device.
+ }
+ }
+
+ /**
+ * Returns the previous port id kept to handle input switching on <Inactive Source>.
+ */
+ int getPrevPortId() {
+ synchronized (mLock) {
+ return mPrevPortId;
+ }
+ }
+
+ /**
+ * Sets the previous port id. INVALID_PORT_ID invalidates it, hence no actions will be
+ * taken for <Inactive Source>.
+ */
+ void setPrevPortId(int portId) {
+ synchronized (mLock) {
+ mPrevPortId = portId;
+ }
+ }
+
+ @ServiceThreadOnly
+ void updateActivePortId(int portId) {
+ assertRunOnServiceThread();
+ // Seq #15
+ if (portId == getActivePortId()) {
+ return;
+ }
+ setPrevPortId(portId);
+ // TODO: Actually switch the physical port here. Handle PAP/PIP as well.
+ // Show OSD port change banner
+ }
+
+ @ServiceThreadOnly
+ void doManualPortSwitching(int portId, IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ // Seq #20
+ if (!isHdmiControlEnabled() || portId == getActivePortId()) {
invokeCallback(callback, HdmiCec.RESULT_INCORRECT_MODE);
return;
}
- // Make sure this call does not stem from <Active Source> message reception, in
- // which case the two ports will be the same.
- if (portId == getActivePortId()) {
- invokeCallback(callback, HdmiCec.RESULT_SUCCESS);
- return;
- }
- setActivePortId(portId);
+ // TODO: Make sure this call does not stem from <Active Source> message reception.
+ setActivePortId(portId);
// TODO: Return immediately if the operation is triggered by <Text/Image View On>
+ // and this is the first notification about the active input after power-on.
// TODO: Handle invalid port id / active input which should be treated as an
- // internal tuner.
+ // internal tuner.
removeAction(RoutingControlAction.class);
@@ -168,6 +235,61 @@
@Override
@ServiceThreadOnly
+ protected boolean handleActiveSource(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ int activePath = HdmiUtils.twoBytesToInt(message.getParams());
+ ActiveSourceHandler.create(this, null).process(message.getSource(), activePath);
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleInactiveSource(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // Seq #10
+
+ // Ignore <Inactive Source> from non-active source device.
+ if (getActiveSource() != message.getSource()) {
+ return true;
+ }
+ if (isInPresetInstallationMode()) {
+ return true;
+ }
+ int portId = getPrevPortId();
+ if (portId != HdmiConstants.INVALID_PORT_ID) {
+ // TODO: Do this only if TV is not showing multiview like PIP/PAP.
+
+ HdmiCecDeviceInfo inactiveSource = getDeviceInfo(message.getSource());
+ if (inactiveSource == null) {
+ return true;
+ }
+ if (mService.pathToPortId(inactiveSource.getPhysicalAddress()) == portId) {
+ return true;
+ }
+ // TODO: Switch the TV freeze mode off
+
+ setActivePortId(portId);
+ doManualPortSwitching(portId, null);
+ setPrevPortId(HdmiConstants.INVALID_PORT_ID);
+ }
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleRequestActiveSource(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // Seq #19
+ int address = getDeviceInfo().getLogicalAddress();
+ if (address == getActiveSource()) {
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildActiveSource(address, getActivePath()));
+ }
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
assertRunOnServiceThread();
HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 53cb81d..ad95181 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -582,7 +582,7 @@
invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
- tv.portSelect(portId, callback);
+ tv.doManualPortSwitching(portId, callback);
}
});
}
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 1783327..e4992d0 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -123,8 +123,6 @@
/**
* @return The state of this Connection.
- *
- * @hide
*/
public final int getState() {
return mState;
@@ -275,6 +273,8 @@
/**
* TODO(santoscordon): Needs updated documentation.
+ *
+ * @hide
*/
public final void conference() {
Log.d(this, "conference");
@@ -285,9 +285,14 @@
* Inform this Connection that the state of its audio output has been changed externally.
*
* @param state The new audio state.
+ * @hide
*/
- public void setAudioState(CallAudioState state) {
+ public final void setAudioState(CallAudioState state) {
Log.d(this, "setAudioState %s", state);
+ mCallAudioState = state;
+ for (Listener l : mListeners) {
+ l.onAudioStateChanged(this, state);
+ }
onSetAudioState(state);
}
@@ -319,18 +324,21 @@
* Returns whether this connection is requesting that the system play a ringback tone
* on its behalf.
*/
- public boolean isRequestingRingback() {
+ public final boolean isRequestingRingback() {
return mRequestingRingback;
}
/**
* Returns whether this connection is a conference connection (has child connections).
*/
- public boolean isConferenceConnection() {
+ public final boolean isConferenceConnection() {
return !mChildConnections.isEmpty();
}
- public void setParentConnection(Connection parentConnection) {
+ /**
+ * TODO(santoscordon): Needs documentation.
+ */
+ public final void setParentConnection(Connection parentConnection) {
Log.d(this, "parenting %s to %s", this, parentConnection);
if (mParentConnection != parentConnection) {
if (mParentConnection != null) {
@@ -347,18 +355,18 @@
}
}
- public Connection getParentConnection() {
+ public final Connection getParentConnection() {
return mParentConnection;
}
- public List<Connection> getChildConnections() {
+ public final List<Connection> getChildConnections() {
return mChildConnections;
}
/**
* Returns whether this connection is capable of being conferenced.
*/
- public boolean isConferenceCapable() {
+ public final boolean isConferenceCapable() {
return mIsConferenceCapable;
}
@@ -367,7 +375,7 @@
*
* @param handle The new handle.
*/
- protected void setHandle(Uri handle) {
+ public final void setHandle(Uri handle) {
Log.d(this, "setHandle %s", handle);
// TODO: Enforce super called
mHandle = handle;
@@ -380,7 +388,7 @@
* Sets state to active (e.g., an ongoing call where two or more parties can actively
* communicate).
*/
- protected void setActive() {
+ public final void setActive() {
setRequestingRingback(false);
setState(State.ACTIVE);
}
@@ -388,21 +396,21 @@
/**
* Sets state to ringing (e.g., an inbound ringing call).
*/
- protected void setRinging() {
+ public final void setRinging() {
setState(State.RINGING);
}
/**
* Sets state to dialing (e.g., dialing an outbound call).
*/
- protected void setDialing() {
+ public final void setDialing() {
setState(State.DIALING);
}
/**
* Sets state to be on hold.
*/
- protected void setOnHold() {
+ public final void setOnHold() {
setState(State.HOLDING);
}
@@ -416,7 +424,7 @@
* {@link android.telephony.DisconnectCause}.
* @param message Optional call-service-provided message about the disconnect.
*/
- protected void setDisconnected(int cause, String message) {
+ public final void setDisconnected(int cause, String message) {
setState(State.DISCONNECTED);
Log.d(this, "Disconnected with cause %d message %s", cause, message);
for (Listener l : mListeners) {
@@ -430,7 +438,7 @@
*
* @param ringback Whether the ringback tone is to be played.
*/
- protected void setRequestingRingback(boolean ringback) {
+ public final void setRequestingRingback(boolean ringback) {
if (mRequestingRingback != ringback) {
mRequestingRingback = ringback;
for (Listener l : mListeners) {
@@ -442,7 +450,7 @@
/**
* TODO(santoscordon): Needs documentation.
*/
- protected void setIsConferenceCapable(boolean isConferenceCapable) {
+ public final void setIsConferenceCapable(boolean isConferenceCapable) {
if (mIsConferenceCapable != isConferenceCapable) {
mIsConferenceCapable = isConferenceCapable;
for (Listener l : mListeners) {
@@ -454,7 +462,7 @@
/**
* TODO(santoscordon): Needs documentation.
*/
- protected void setDestroyed() {
+ public final void setDestroyed() {
// It is possible that onDestroy() will trigger the listener to remove itself which will
// result in a concurrent modification exception. To counteract this we make a copy of the
// listeners and iterate on that.
@@ -466,46 +474,32 @@
}
/**
- * Notifies this Connection and listeners that the {@link #getCallAudioState()} property
- * has a new value.
- *
- * @param state The new call audio state.
- */
- protected void onSetAudioState(CallAudioState state) {
- // TODO: Enforce super called
- mCallAudioState = state;
- for (Listener l : mListeners) {
- l.onAudioStateChanged(this, state);
- }
- }
-
- /**
* Notifies this Connection and listeners of a change in the current signal levels
* for the underlying data transport.
*
* @param details A {@link android.os.Bundle} containing details of the current level.
*/
- protected void onSetSignal(Bundle details) {
- // TODO: Enforce super called
+ public final void setSignal(Bundle details) {
for (Listener l : mListeners) {
l.onSignalChanged(this, details);
}
}
/**
+ * Notifies this Connection and listeners that the {@link #getCallAudioState()} property
+ * has a new value.
+ *
+ * @param state The new call audio state.
+ */
+ protected void onSetAudioState(CallAudioState state) {}
+
+ /**
* Notifies this Connection of an internal state change. This method is called before the
- * state is actually changed. Overriding implementations must call
- * {@code super.onSetState(state)}.
+ * state is actually changed.
*
* @param state The new state, a {@link Connection.State} member.
*/
- protected void onSetState(int state) {
- // TODO: Enforce super called
- this.mState = state;
- for (Listener l : mListeners) {
- l.onStateChanged(this, state);
- }
- }
+ protected void onSetState(int state) {}
/**
* Notifies this Connection of a request to play a DTMF tone.
@@ -585,6 +579,10 @@
private void setState(int state) {
Log.d(this, "setState: %s", stateToString(state));
+ this.mState = state;
+ for (Listener l : mListeners) {
+ l.onStateChanged(this, state);
+ }
onSetState(state);
}
}