Merge "Cap the size of app-defined permission trees"
diff --git a/Android.mk b/Android.mk
index d279704..45e9c76 100644
--- a/Android.mk
+++ b/Android.mk
@@ -136,6 +136,8 @@
core/java/android/hardware/ISerialManager.aidl \
core/java/android/hardware/display/IDisplayManager.aidl \
core/java/android/hardware/display/IDisplayManagerCallback.aidl \
+ core/java/android/hardware/hdmi/IHdmiCecListener.aidl \
+ core/java/android/hardware/hdmi/IHdmiCecService.aidl \
core/java/android/hardware/input/IInputManager.aidl \
core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
core/java/android/hardware/location/IFusedLocationHardware.aidl \
diff --git a/api/current.txt b/api/current.txt
index 09102fb..a1628ca 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4202,6 +4202,19 @@
method public int describeContents();
method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String CATEGORY_ALARM = "alarm";
+ field public static final java.lang.String CATEGORY_CALL = "call";
+ field public static final java.lang.String CATEGORY_EMAIL = "email";
+ field public static final java.lang.String CATEGORY_ERROR = "err";
+ field public static final java.lang.String CATEGORY_EVENT = "event";
+ field public static final java.lang.String CATEGORY_MESSAGE = "msg";
+ field public static final java.lang.String CATEGORY_PROGRESS = "progress";
+ field public static final java.lang.String CATEGORY_PROMO = "promo";
+ field public static final java.lang.String CATEGORY_SERVICE = "service";
+ field public static final java.lang.String CATEGORY_SOCIAL = "social";
+ field public static final java.lang.String CATEGORY_STATUS = "status";
+ field public static final java.lang.String CATEGORY_SYSTEM = "sys";
+ field public static final java.lang.String CATEGORY_TRANSPORT = "transport";
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int DEFAULT_ALL = -1; // 0xffffffff
field public static final int DEFAULT_LIGHTS = 4; // 0x4
@@ -4246,6 +4259,7 @@
field public android.app.Notification.Action[] actions;
field public int audioStreamType;
field public android.widget.RemoteViews bigContentView;
+ field public java.lang.String category;
field public android.app.PendingIntent contentIntent;
field public android.widget.RemoteViews contentView;
field public int defaults;
@@ -4306,6 +4320,7 @@
method public android.os.Bundle getExtras();
method public deprecated android.app.Notification getNotification();
method public android.app.Notification.Builder setAutoCancel(boolean);
+ method public android.app.Notification.Builder setCategory(java.lang.String);
method public android.app.Notification.Builder setContent(android.widget.RemoteViews);
method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
@@ -11781,6 +11796,54 @@
}
+package android.hardware.hdmi {
+
+ public final class HdmiCec {
+ method public static java.lang.String getDefaultDeviceName(int);
+ method public static int getTypeFromAddress(int);
+ method public static boolean isValidAddress(int);
+ method public static boolean isValidType(int);
+ field public static final int ADDR_AUDIO_SYSTEM = 5; // 0x5
+ field public static final int ADDR_BROADCAST = 15; // 0xf
+ field public static final int ADDR_FREE_USE = 14; // 0xe
+ field public static final int ADDR_INVALID = -1; // 0xffffffff
+ field public static final int ADDR_PLAYBACK_1 = 4; // 0x4
+ field public static final int ADDR_PLAYBACK_2 = 8; // 0x8
+ field public static final int ADDR_PLAYBACK_3 = 11; // 0xb
+ field public static final int ADDR_RECORDER_1 = 1; // 0x1
+ field public static final int ADDR_RECORDER_2 = 2; // 0x2
+ field public static final int ADDR_RECORDER_3 = 9; // 0x9
+ field public static final int ADDR_RESERVED_1 = 12; // 0xc
+ field public static final int ADDR_RESERVED_2 = 13; // 0xd
+ field public static final int ADDR_TUNER_1 = 3; // 0x3
+ field public static final int ADDR_TUNER_2 = 6; // 0x6
+ field public static final int ADDR_TUNER_3 = 7; // 0x7
+ field public static final int ADDR_TUNER_4 = 10; // 0xa
+ field public static final int ADDR_TV = 0; // 0x0
+ field public static final int ADDR_UNREGISTERED = 15; // 0xf
+ field public static final int DEVICE_AUDIO_SYSTEM = 5; // 0x5
+ field public static final int DEVICE_INACTIVE = -1; // 0xffffffff
+ field public static final int DEVICE_PLAYBACK = 4; // 0x4
+ field public static final int DEVICE_RECORDER = 1; // 0x1
+ field public static final int DEVICE_RESERVED = 2; // 0x2
+ field public static final int DEVICE_TUNER = 3; // 0x3
+ field public static final int DEVICE_TV = 0; // 0x0
+ field public static final int MESSAGE_ACTIVE_SOURCE = 157; // 0x9d
+ }
+
+ public final class HdmiCecMessage implements android.os.Parcelable {
+ ctor public HdmiCecMessage(int, int, int, byte[]);
+ method public int describeContents();
+ method public int getDestination();
+ method public int getOpcode();
+ method public byte[] getParams();
+ method public int getSource();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+}
+
package android.hardware.input {
public final class InputManager {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 12a8ff6..2e8fbb6 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -444,41 +444,75 @@
public static final int VISIBILITY_SECRET = -1;
/**
- * @hide
- * Notification type: incoming call (voice or video) or similar synchronous communication request.
+ * Notification category: incoming call (voice or video) or similar synchronous communication request.
*/
- public static final String KIND_CALL = "android.call";
+ public static final String CATEGORY_CALL = "call";
/**
- * @hide
- * Notification type: incoming direct message (SMS, instant message, etc.).
+ * Notification category: incoming direct message (SMS, instant message, etc.).
*/
- public static final String KIND_MESSAGE = "android.message";
+ public static final String CATEGORY_MESSAGE = "msg";
/**
- * @hide
- * Notification type: asynchronous bulk message (email).
+ * Notification category: asynchronous bulk message (email).
*/
- public static final String KIND_EMAIL = "android.email";
+ public static final String CATEGORY_EMAIL = "email";
/**
- * @hide
- * Notification type: calendar event.
+ * Notification category: calendar event.
*/
- public static final String KIND_EVENT = "android.event";
+ public static final String CATEGORY_EVENT = "event";
/**
- * @hide
- * Notification type: promotion or advertisement.
+ * Notification category: promotion or advertisement.
*/
- public static final String KIND_PROMO = "android.promo";
+ public static final String CATEGORY_PROMO = "promo";
/**
- * @hide
- * If this notification matches of one or more special types (see the <code>KIND_*</code>
- * constants), add them here, best match first.
+ * Notification category: alarm or timer.
*/
- public String[] kind;
+ public static final String CATEGORY_ALARM = "alarm";
+
+ /**
+ * Notification category: progress of a long-running background operation.
+ */
+ public static final String CATEGORY_PROGRESS = "progress";
+
+ /**
+ * Notification category: social network or sharing update.
+ */
+ public static final String CATEGORY_SOCIAL = "social";
+
+ /**
+ * Notification category: error in background operation or authentication status.
+ */
+ public static final String CATEGORY_ERROR = "err";
+
+ /**
+ * Notification category: media transport control for playback.
+ */
+ public static final String CATEGORY_TRANSPORT = "transport";
+
+ /**
+ * Notification category: system or device status update. Reserved for system use.
+ */
+ public static final String CATEGORY_SYSTEM = "sys";
+
+ /**
+ * Notification category: indication of running background service.
+ */
+ public static final String CATEGORY_SERVICE = "service";
+
+ /**
+ * Notification category: ongoing information about device or contextual status.
+ */
+ public static final String CATEGORY_STATUS = "status";
+
+ /**
+ * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
+ * that best describes this Notification. May be used by the system for ranking and filtering.
+ */
+ public String category;
/**
* Additional semantic data to be carried around with this Notification.
@@ -619,6 +653,13 @@
public static final String EXTRA_BUILDER_REMOTE_VIEWS = "android.builderRemoteViews";
/**
+ * Allow certain system-generated notifications to appear before the device is provisioned.
+ * Only available to notifications coming from the android package.
+ * @hide
+ */
+ public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
+
+ /**
* Value for {@link #EXTRA_AS_HEADS_UP}.
* @hide
*/
@@ -815,7 +856,7 @@
priority = parcel.readInt();
- kind = parcel.createStringArray(); // may set kind to null
+ category = parcel.readString();
extras = parcel.readBundle(); // may be null
@@ -890,12 +931,7 @@
that.priority = this.priority;
- final String[] thiskind = this.kind;
- if (thiskind != null) {
- final int N = thiskind.length;
- final String[] thatkind = that.kind = new String[N];
- System.arraycopy(thiskind, 0, thatkind, 0, N);
- }
+ that.category = this.category;
if (this.extras != null) {
try {
@@ -1044,7 +1080,7 @@
parcel.writeInt(priority);
- parcel.writeStringArray(kind); // ok for null
+ parcel.writeString(category);
parcel.writeBundle(extras); // null ok
@@ -1180,16 +1216,7 @@
sb.append(Integer.toHexString(this.defaults));
sb.append(" flags=0x");
sb.append(Integer.toHexString(this.flags));
- sb.append(" kind=[");
- if (this.kind == null) {
- sb.append("null");
- } else {
- for (int i=0; i<this.kind.length; i++) {
- if (i>0) sb.append(",");
- sb.append(this.kind[i]);
- }
- }
- sb.append("]");
+ sb.append(" category="); sb.append(this.category);
if (actions != null) {
sb.append(" ");
sb.append(actions.length);
@@ -1271,7 +1298,7 @@
private int mProgressMax;
private int mProgress;
private boolean mProgressIndeterminate;
- private ArrayList<String> mKindList = new ArrayList<String>(1);
+ private String mCategory;
private Bundle mExtras;
private int mPriority;
private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
@@ -1679,14 +1706,12 @@
}
/**
- * @hide
+ * Set the notification category.
*
- * Add a kind (category) to this notification. Optional.
- *
- * @see Notification#kind
+ * @see Notification#category
*/
- public Builder addKind(String k) {
- mKindList.add(k);
+ public Builder setCategory(String category) {
+ mCategory = category;
return this;
}
@@ -2010,12 +2035,7 @@
if ((mDefaults & DEFAULT_LIGHTS) != 0) {
n.flags |= FLAG_SHOW_LIGHTS;
}
- if (mKindList.size() > 0) {
- n.kind = new String[mKindList.size()];
- mKindList.toArray(n.kind);
- } else {
- n.kind = null;
- }
+ n.category = mCategory;
n.priority = mPriority;
if (mActions.size() > 0) {
n.actions = new Action[mActions.size()];
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
new file mode 100644
index 0000000..c95431a
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -0,0 +1,190 @@
+/*
+ * 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.hdmi;
+
+/**
+ * Defines constants and utility methods related to HDMI-CEC protocol.
+ */
+public final class HdmiCec {
+
+ /** TV device type. */
+ public static final int DEVICE_TV = 0;
+
+ /** Recording device type. */
+ public static final int DEVICE_RECORDER = 1;
+
+ /** Device type reserved for future usage. */
+ public static final int DEVICE_RESERVED = 2;
+
+ /** Tuner device type. */
+ public static final int DEVICE_TUNER = 3;
+
+ /** Playback device type. */
+ public static final int DEVICE_PLAYBACK = 4;
+
+ /** Audio system device type. */
+ public static final int DEVICE_AUDIO_SYSTEM = 5;
+
+ // Value indicating the device is not an active source.
+ public static final int DEVICE_INACTIVE = -1;
+
+ /** Logical address for TV */
+ public static final int ADDR_TV = 0;
+
+ /** Logical address for recorder 1 */
+ public static final int ADDR_RECORDER_1 = 1;
+
+ /** Logical address for recorder 2 */
+ public static final int ADDR_RECORDER_2 = 2;
+
+ /** Logical address for tuner 1 */
+ public static final int ADDR_TUNER_1 = 3;
+
+ /** Logical address for playback 1 */
+ public static final int ADDR_PLAYBACK_1 = 4;
+
+ /** Logical address for audio system */
+ public static final int ADDR_AUDIO_SYSTEM = 5;
+
+ /** Logical address for tuner 2 */
+ public static final int ADDR_TUNER_2 = 6;
+
+ /** Logical address for tuner 3 */
+ public static final int ADDR_TUNER_3 = 7;
+
+ /** Logical address for playback 2 */
+ public static final int ADDR_PLAYBACK_2 = 8;
+
+ /** Logical address for recorder 3 */
+ public static final int ADDR_RECORDER_3 = 9;
+
+ /** Logical address for tuner 4 */
+ public static final int ADDR_TUNER_4 = 10;
+
+ /** Logical address for playback 3 */
+ public static final int ADDR_PLAYBACK_3 = 11;
+
+ /** Logical address reserved for future usage */
+ public static final int ADDR_RESERVED_1 = 12;
+
+ /** Logical address reserved for future usage */
+ public static final int ADDR_RESERVED_2 = 13;
+
+ /** Logical address for TV other than the one assigned with {@link #ADDR_TV} */
+ public static final int ADDR_FREE_USE = 14;
+
+ /** Logical address for devices to which address cannot be allocated */
+ public static final int ADDR_UNREGISTERED = 15;
+
+ /** Logical address used in the destination address field for broadcast messages */
+ public static final int ADDR_BROADCAST = 15;
+
+ /** Logical address used to indicate it is not initialized or invalid. */
+ public static final int ADDR_INVALID = -1;
+
+ // TODO: Complete the list of CEC messages definition.
+ public static final int MESSAGE_ACTIVE_SOURCE = 0x9D;
+
+ private static final int[] ADDRESS_TO_TYPE = {
+ DEVICE_TV, // ADDR_TV
+ DEVICE_RECORDER, // ADDR_RECORDER_1
+ DEVICE_RECORDER, // ADDR_RECORDER_2
+ DEVICE_TUNER, // ADDR_TUNER_1
+ DEVICE_PLAYBACK, // ADDR_PLAYBACK_1
+ DEVICE_AUDIO_SYSTEM, // ADDR_AUDIO_SYSTEM
+ DEVICE_TUNER, // ADDR_TUNER_2
+ DEVICE_TUNER, // ADDR_TUNER_3
+ DEVICE_PLAYBACK, // ADDR_PLAYBACK_2
+ DEVICE_RECORDER, // ADDR_RECORDER_3
+ DEVICE_TUNER, // ADDR_TUNER_4
+ DEVICE_PLAYBACK, // ADDR_PLAYBACK_3
+ };
+
+ private static final String[] DEFAULT_NAMES = {
+ "TV",
+ "Recorder_1",
+ "Recorder_2",
+ "Tuner_1",
+ "Playback_1",
+ "AudioSystem",
+ "Tuner_2",
+ "Tuner_3",
+ "Playback_2",
+ "Recorder_3",
+ "Tuner_4",
+ "Playback_3",
+ };
+
+ private HdmiCec() { } // Prevents instantiation.
+
+ /**
+ * Check if the given type is valid. A valid type is one of the actual
+ * logical device types defined in the standard ({@link #DEVICE_TV},
+ * {@link #DEVICE_PLAYBACK}, {@link #DEVICE_TUNER}, {@link #DEVICE_RECORDER},
+ * and {@link #DEVICE_AUDIO_SYSTEM}).
+ *
+ * @param type device type
+ * @return true if the given type is valid
+ */
+ public static boolean isValidType(int type) {
+ return (DEVICE_TV <= type && type <= DEVICE_AUDIO_SYSTEM)
+ && type != DEVICE_RESERVED;
+ }
+
+ /**
+ * Check if the given logical address is valid. A logical address is valid
+ * if it is one allocated for an actual device which allows communication
+ * with other logical devices.
+ *
+ * @param address logical address
+ * @return true if the given address is valid
+ */
+ public static boolean isValidAddress(int address) {
+ // TODO: We leave out the address 'free use(14)' for now. Check this later
+ // again to make sure it is a valid address for communication.
+ return (ADDR_TV <= address && address <= ADDR_PLAYBACK_3);
+ }
+
+ /**
+ * Return the device type for the given logical address.
+ *
+ * @param address logical address
+ * @return device type for the given logical address; DEVICE_INACTIVE
+ * if the address is not valid.
+ */
+ public static int getTypeFromAddress(int address) {
+ if (isValidAddress(address)) {
+ return ADDRESS_TO_TYPE[address];
+ }
+ return DEVICE_INACTIVE;
+ }
+
+ /**
+ * Return the default device name for a logical address. This is the name
+ * by which the logical device is known to others until a name is
+ * set explicitly using HdmiCecService.setOsdName.
+ *
+ * @param address logical address
+ * @return default device name; empty string if the address is not valid
+ */
+ public static String getDefaultDeviceName(int address) {
+ if (isValidAddress(address)) {
+ return DEFAULT_NAMES[address];
+ }
+ return "";
+ }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.aidl b/core/java/android/hardware/hdmi/HdmiCecMessage.aidl
new file mode 100644
index 0000000..6687ba4
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.hdmi;
+
+parcelable HdmiCecMessage;
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java
new file mode 100644
index 0000000..be94d97
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java
@@ -0,0 +1,145 @@
+/*
+ * 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.hdmi;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * A class to encapsulate HDMI-CEC message used for the devices connected via
+ * HDMI cable to communicate with one another. A message is defined by its
+ * source and destination address, command (or opcode), and optional parameters.
+ */
+public final class HdmiCecMessage implements Parcelable {
+
+ private static final int MAX_MESSAGE_LENGTH = 16;
+
+ private final int mSource;
+ private final int mDestination;
+
+ private final int mOpcode;
+ private final byte[] mParams;
+
+ /**
+ * Constructor.
+ */
+ public HdmiCecMessage(int source, int destination, int opcode, byte[] params) {
+ mSource = source;
+ mDestination = destination;
+ mOpcode = opcode;
+ mParams = Arrays.copyOf(params, params.length);
+ }
+
+ /**
+ * Return the source address field of the message. It is the logical address
+ * of the device which generated the message.
+ *
+ * @return source address
+ */
+ public int getSource() {
+ return mSource;
+ }
+
+ /**
+ * Return the destination address field of the message. It is the logical address
+ * of the device to which the message is sent.
+ *
+ * @return destination address
+ */
+ public int getDestination() {
+ return mDestination;
+ }
+
+ /**
+ * Return the opcode field of the message. It is the type of the message that
+ * tells the destination device what to do.
+ *
+ * @return opcode
+ */
+ public int getOpcode() {
+ return mOpcode;
+ }
+
+ /**
+ * Return the parameter field of the message. The contents of parameter varies
+ * from opcode to opcode, and is used together with opcode to describe
+ * the action for the destination device to take.
+ *
+ * @return parameter
+ */
+ public byte[] getParams() {
+ return mParams;
+ }
+
+ /**
+ * Describe the kinds of special objects contained in this Parcelable's
+ * marshalled representation.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSource);
+ dest.writeInt(mDestination);
+ dest.writeInt(mOpcode);
+ dest.writeInt(mParams.length);
+ dest.writeByteArray(mParams);
+ }
+
+ public static final Parcelable.Creator<HdmiCecMessage> CREATOR
+ = new Parcelable.Creator<HdmiCecMessage>() {
+ /**
+ * Rebuild a HdmiCecMessage previously stored with writeToParcel().
+ * @param p HdmiCecMessage object to read the Rating from
+ * @return a new HdmiCecMessage created from the data in the parcel
+ */
+ public HdmiCecMessage createFromParcel(Parcel p) {
+ int source = p.readInt();
+ int destination = p.readInt();
+ int opcode = p.readInt();
+ byte[] params = new byte[p.readInt()];
+ p.readByteArray(params);
+ return new HdmiCecMessage(source, destination, opcode, params);
+ }
+ public HdmiCecMessage[] newArray(int size) {
+ return new HdmiCecMessage[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ StringBuffer s = new StringBuffer();
+ s.append(String.format("src: %d dst: %d op: %2X params: ", mSource, mDestination, mOpcode));
+ for (byte data : mParams) {
+ s.append(String.format("%02X ", data));
+ }
+ return s.toString();
+ }
+}
+
diff --git a/core/java/android/hardware/hdmi/IHdmiCecListener.aidl b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl
new file mode 100644
index 0000000..d281ce6
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.hdmi.HdmiCecMessage;
+
+/**
+ * Interface definition for HdmiCecService to do interprocess communcation.
+ *
+ * @hide
+ */
+oneway interface IHdmiCecListener {
+ void onMessageReceived(in HdmiCecMessage message);
+ void onCableStatusChanged(in boolean connected);
+}
diff --git a/core/java/android/hardware/hdmi/IHdmiCecService.aidl b/core/java/android/hardware/hdmi/IHdmiCecService.aidl
new file mode 100644
index 0000000..6fefcf8
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiCecService.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiCecListener;
+import android.os.IBinder;
+
+/**
+ * Binder interface that components running in the appplication process
+ * will use to enable HDMI-CEC protocol exchange with other devices.
+ *
+ * @hide
+ */
+interface IHdmiCecService {
+ IBinder allocateLogicalDevice(int type, IHdmiCecListener listener);
+ void removeServiceListener(IBinder b, IHdmiCecListener listener);
+ void setOsdName(IBinder b, String name);
+ void sendActiveSource(IBinder b);
+ void sendInactiveSource(IBinder b);
+ void sendImageViewOn(IBinder b);
+ void sendTextViewOn(IBinder b);
+ void sendGiveDevicePowerStatus(IBinder b, int address);
+ void sendMessage(IBinder b, in HdmiCecMessage message);
+}
+
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index b97734e..4180860 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -694,6 +694,36 @@
return _result;
}
+ public String getPassword() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ String _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_getPassword, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readString();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
+ public void clearPassword() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ String _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_clearPassword, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
public StorageVolume[] getVolumeList() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
@@ -846,7 +876,11 @@
static final int TRANSACTION_mkdirs = IBinder.FIRST_CALL_TRANSACTION + 34;
- static final int TRANSACTION_getPasswordType = IBinder.FIRST_CALL_TRANSACTION + 36;
+ static final int TRANSACTION_getPasswordType = IBinder.FIRST_CALL_TRANSACTION + 35;
+
+ static final int TRANSACTION_getPassword = IBinder.FIRST_CALL_TRANSACTION + 36;
+
+ static final int TRANSACTION_clearPassword = IBinder.FIRST_CALL_TRANSACTION + 37;
/**
* Cast an IBinder object into an IMountService interface, generating a
@@ -1208,6 +1242,19 @@
reply.writeInt(result);
return true;
}
+ case TRANSACTION_getPassword: {
+ data.enforceInterface(DESCRIPTOR);
+ String result = getPassword();
+ reply.writeNoException();
+ reply.writeString(result);
+ return true;
+ }
+ case TRANSACTION_clearPassword: {
+ data.enforceInterface(DESCRIPTOR);
+ clearPassword();
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
}
@@ -1446,4 +1493,15 @@
* @return PasswordType
*/
public int getPasswordType() throws RemoteException;
+
+ /**
+ * Get password from vold
+ * @return password or empty string
+ */
+ public String getPassword() throws RemoteException;
+
+ /**
+ * Securely clear password from vold
+ */
+ public void clearPassword() throws RemoteException;
}
diff --git a/core/java/android/provider/SearchIndexableData.java b/core/java/android/provider/SearchIndexableData.java
index 6381884..719dcea 100644
--- a/core/java/android/provider/SearchIndexableData.java
+++ b/core/java/android/provider/SearchIndexableData.java
@@ -21,15 +21,16 @@
import java.util.Locale;
/**
- * The Indexable data for Search. This abstract class defines the common parts for all search
- * indexable data.
+ * The Indexable data for Search.
+ *
+ * This abstract class defines the common parts for all search indexable data.
*
* @hide
*/
public abstract class SearchIndexableData {
/**
- * The context for the data. Will usually allow to retrieve some resources.
+ * The context for the data. Will usually allow retrieving some resources.
*
* @see Context
*/
@@ -41,6 +42,11 @@
public Locale locale;
/**
+ * Tells if the data will be included into the search results. This is application specific.
+ */
+ public boolean enabled;
+
+ /**
* The rank for the data. This is application specific.
*/
public int rank;
@@ -103,6 +109,7 @@
* Default constructor.
*/
public SearchIndexableData() {
+ enabled = true;
}
/**
@@ -113,5 +120,6 @@
public SearchIndexableData(Context ctx) {
context = ctx;
locale = Locale.getDefault();
+ enabled = true;
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1d6e998..a94a671 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -659,14 +659,14 @@
mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_FLUSH_LAYER_UPDATES));
}
- public void attachFunctor(int functor) {
+ public void attachFunctor(long functor) {
//noinspection SimplifiableIfStatement
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
}
}
- public void detachFunctor(int functor) {
+ public void detachFunctor(long functor) {
mBlockResizeBuffer = true;
if (mAttachInfo.mHardwareRenderer != null) {
mAttachInfo.mHardwareRenderer.detachFunctor(functor);
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index 52f9c0b..b05225b 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -787,7 +787,7 @@
return;
}
- mVibrator.vibrate(VIBRATE_DURATION);
+ mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
}
protected void onRemoteVolumeChanged(int streamType, int flags) {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index bd203c8..858cf4b 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -124,6 +124,8 @@
* to prepareAddWindow() until removeWindow().
*/
public interface WindowState {
+ public final static int UNKNOWN_TASK_ID = -1;
+
/**
* Return the uid of the app that owns this window.
*/
@@ -272,7 +274,7 @@
* Get the layer at which this window's surface will be Z-ordered.
*/
public int getSurfaceLayer();
-
+
/**
* Return the token for the application (actually activity) that owns
* this window. May return null for system windows.
@@ -282,6 +284,11 @@
public IApplicationToken getAppToken();
/**
+ * Return the taskId of the task that owns this window.
+ */
+ public int getTaskId();
+
+ /**
* Return true if, at any point, the application token associated with
* this window has actually displayed any windows. This is most useful
* with the "starting up" window to determine if any windows were
diff --git a/core/java/android/view/animation/ClipRectAnimation.java b/core/java/android/view/animation/ClipRectAnimation.java
new file mode 100644
index 0000000..2361501
--- /dev/null
+++ b/core/java/android/view/animation/ClipRectAnimation.java
@@ -0,0 +1,59 @@
+/*
+ * 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.view.animation;
+
+import android.graphics.Rect;
+
+/**
+ * An animation that controls the clip of an object. See the
+ * {@link android.view.animation full package} description for details and
+ * sample code.
+ *
+ * @hide
+ */
+public class ClipRectAnimation extends Animation {
+ private Rect mFromRect = new Rect();
+ private Rect mToRect = new Rect();
+
+ /**
+ * Constructor to use when building a ClipRectAnimation from code
+ *
+ * @param fromClip the clip rect to animate from
+ * @param toClip the clip rect to animate to
+ */
+ public ClipRectAnimation(Rect fromClip, Rect toClip) {
+ if (fromClip == null || toClip == null) {
+ throw new RuntimeException("Expected non-null animation clip rects");
+ }
+ mFromRect.set(fromClip);
+ mToRect.set(toClip);
+ }
+
+ @Override
+ protected void applyTransformation(float it, Transformation tr) {
+ int l = mFromRect.left + (int) ((mToRect.left - mFromRect.left) * it);
+ int t = mFromRect.top + (int) ((mToRect.top - mFromRect.top) * it);
+ int r = mFromRect.right + (int) ((mToRect.right - mFromRect.right) * it);
+ int b = mFromRect.bottom + (int) ((mToRect.bottom - mFromRect.bottom) * it);
+ tr.setClipRect(l, t, r, b);
+ }
+
+ @Override
+ public boolean willChangeTransformationMatrix() {
+ return false;
+ }
+}
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index 890909b..2f4fe73 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -17,6 +17,7 @@
package android.view.animation;
import android.graphics.Matrix;
+import android.graphics.Rect;
import java.io.PrintWriter;
@@ -47,6 +48,9 @@
protected float mAlpha;
protected int mTransformationType;
+ private boolean mHasClipRect;
+ private Rect mClipRect = new Rect();
+
/**
* Creates a new transformation with alpha = 1 and the identity matrix.
*/
@@ -65,6 +69,8 @@
} else {
mMatrix.reset();
}
+ mClipRect.setEmpty();
+ mHasClipRect = false;
mAlpha = 1.0f;
mTransformationType = TYPE_BOTH;
}
@@ -98,9 +104,15 @@
public void set(Transformation t) {
mAlpha = t.getAlpha();
mMatrix.set(t.getMatrix());
+ if (t.mHasClipRect) {
+ setClipRect(t.getClipRect());
+ } else {
+ mHasClipRect = false;
+ mClipRect.setEmpty();
+ }
mTransformationType = t.getTransformationType();
}
-
+
/**
* Apply this Transformation to an existing Transformation, e.g. apply
* a scale effect to something that has already been rotated.
@@ -109,6 +121,9 @@
public void compose(Transformation t) {
mAlpha *= t.getAlpha();
mMatrix.preConcat(t.getMatrix());
+ if (t.mHasClipRect) {
+ setClipRect(t.getClipRect());
+ }
}
/**
@@ -119,6 +134,9 @@
public void postCompose(Transformation t) {
mAlpha *= t.getAlpha();
mMatrix.postConcat(t.getMatrix());
+ if (t.mHasClipRect) {
+ setClipRect(t.getClipRect());
+ }
}
/**
@@ -138,6 +156,39 @@
}
/**
+ * Sets the current Transform's clip rect
+ * @hide
+ */
+ public void setClipRect(Rect r) {
+ setClipRect(r.left, r.top, r.right, r.bottom);
+ }
+
+ /**
+ * Sets the current Transform's clip rect
+ * @hide
+ */
+ public void setClipRect(int l, int t, int r, int b) {
+ mClipRect.set(l, t, r, b);
+ mHasClipRect = true;
+ }
+
+ /**
+ * Returns the current Transform's clip rect
+ * @hide
+ */
+ public Rect getClipRect() {
+ return mClipRect;
+ }
+
+ /**
+ * Returns whether the current Transform's clip rect is set
+ * @hide
+ */
+ public boolean hasClipRect() {
+ return mHasClipRect;
+ }
+
+ /**
* @return The degree of transparency
*/
public float getAlpha() {
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 91056f1..9501f92 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -28,6 +28,7 @@
boolean checkPattern(in String pattern, int userId);
void setLockPassword(in String password, int userId);
boolean checkPassword(in String password, int userId);
+ boolean checkVoldPassword(int userId);
boolean havePattern(int userId);
boolean havePassword(int userId);
void removeUser(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 2d79491..e5aaf7e 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -313,6 +313,20 @@
}
/**
+ * Check to see if vold already has the password.
+ * Note that this also clears vold's copy of the password.
+ * @return Whether the vold password matches or not.
+ */
+ public boolean checkVoldPassword() {
+ final int userId = getCurrentOrCallingUserId();
+ try {
+ return getLockSettings().checkVoldPassword(userId);
+ } catch (RemoteException re) {
+ return false;
+ }
+ }
+
+ /**
* Check to see if a password matches any of the passwords stored in the
* password history.
*
diff --git a/core/java/com/android/internal/widget/RotarySelector.java b/core/java/com/android/internal/widget/RotarySelector.java
index f856027..64ce918 100644
--- a/core/java/com/android/internal/widget/RotarySelector.java
+++ b/core/java/com/android/internal/widget/RotarySelector.java
@@ -24,6 +24,7 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
+import android.media.AudioManager;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
@@ -34,7 +35,9 @@
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.animation.DecelerateInterpolator;
+
import static android.view.animation.AnimationUtils.currentAnimationTimeMillis;
+
import com.android.internal.R;
@@ -676,7 +679,7 @@
mVibrator = (android.os.Vibrator) getContext()
.getSystemService(Context.VIBRATOR_SERVICE);
}
- mVibrator.vibrate(duration);
+ mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
}
}
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index aebc4f6..deb0fd7 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -21,6 +21,7 @@
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
@@ -821,7 +822,7 @@
mVibrator = (android.os.Vibrator) getContext()
.getSystemService(Context.VIBRATOR_SERVICE);
}
- mVibrator.vibrate(duration);
+ mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
}
}
diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java
index 0c5993b..86f14b3 100644
--- a/core/java/com/android/internal/widget/WaveView.java
+++ b/core/java/com/android/internal/widget/WaveView.java
@@ -25,6 +25,7 @@
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
+import android.media.AudioManager;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
@@ -582,7 +583,7 @@
mVibrator = (android.os.Vibrator) getContext()
.getSystemService(Context.VIBRATOR_SERVICE);
}
- mVibrator.vibrate(duration);
+ mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
}
}
diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
index 93ea5b3..772dc5f 100644
--- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
@@ -30,6 +30,7 @@
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.Vibrator;
@@ -564,7 +565,7 @@
mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
UserHandle.USER_CURRENT) != 0;
if (mVibrator != null && hapticEnabled) {
- mVibrator.vibrate(mVibrationDuration);
+ mVibrator.vibrate(mVibrationDuration, AudioManager.STREAM_SYSTEM);
}
}
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index e22d1e8..4648f39 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -31,6 +31,7 @@
import android.graphics.Canvas;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.Vibrator;
@@ -599,7 +600,7 @@
mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
UserHandle.USER_CURRENT) != 0;
if (mVibrator != null && hapticEnabled) {
- mVibrator.vibrate(mVibrationDuration);
+ mVibrator.vibrate(mVibrationDuration, AudioManager.STREAM_SYSTEM);
}
}
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index a64d3ba..3a5b566 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -606,7 +606,7 @@
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz,
- jbyteArray javaBytes, jint byteOffset, jint offsetInBytes, jint sizeInBytes,
+ jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
jint javaAudioFormat, jboolean isWriteBlocking) {
//ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
// offsetInBytes, sizeInBytes);
@@ -623,7 +623,7 @@
return AUDIOTRACK_ERROR_BAD_VALUE;
}
- jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get() + byteOffset, offsetInBytes,
+ jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
return written;
@@ -922,7 +922,7 @@
{"native_release", "()V", (void *)android_media_AudioTrack_release},
{"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte},
{"native_write_native_bytes",
- "(Ljava/lang/Object;IIIIZ)I",
+ "(Ljava/lang/Object;IIIZ)I",
(void *)android_media_AudioTrack_write_native_bytes},
{"native_write_short", "([SIII)I", (void *)android_media_AudioTrack_write_short},
{"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4e1efc6..2168bd1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -684,6 +684,12 @@
android:label="@string/permlab_installLocationProvider"
android:description="@string/permdesc_installLocationProvider" />
+ <!-- Allows HDMI-CEC service to access device and configuration files.
+ @hide This should only be used by HDMI-CEC service.
+ -->
+ <permission android:name="android.permission.HDMI_CEC"
+ android:protectionLevel="signatureOrSystem" />
+
<!-- Allows an application to use location features in hardware,
such as the geofencing api.
<p>Not for use by third-party applications. -->
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index dee8705..40c6797 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1207,14 +1207,11 @@
* In streaming mode, the blocking behavior will depend on the write mode.
* @param audioData the buffer that holds the data to play, starting at the position reported
* by <code>audioData.position()</code>.
- * <BR>Note that this method will not update the position in this buffer, therefore when
- * writing a loop to write all the data in the buffer, you should increment the
- * <code>offsetInBytes</code> parameter at each pass by the amount that was previously
- * written for this buffer.
- * @param offsetInBytes offset to read from in bytes (note this differs from
- * <code>audioData.position()</code>).
- * @param sizeInBytes number of bytes to read (note this differs from
- * <code>audioData.remaining()</code>).
+ * <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
+ * have been advanced to reflect the amount of data that was successfully written to
+ * the AudioTrack.
+ * @param sizeInBytes number of bytes to write.
+ * <BR>Note this may differ from <code>audioData.remaining()</code>, but cannot exceed it.
* @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
* effect in static mode.
* <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -1224,7 +1221,7 @@
* @return 0 or a positive number of bytes that were written, or
* {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
*/
- public int write(ByteBuffer audioData, int offsetInBytes, int sizeInBytes,
+ public int write(ByteBuffer audioData, int sizeInBytes,
@WriteMode int writeMode) {
if (mState == STATE_UNINITIALIZED) {
@@ -1237,22 +1234,19 @@
return ERROR_BAD_VALUE;
}
- if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
- || (offsetInBytes + sizeInBytes < 0) // detect integer overflow
- || (offsetInBytes + sizeInBytes > audioData.remaining())) {
- Log.e(TAG, "AudioTrack.write() called with invalid size/offset values");
+ if ( (audioData == null) || (sizeInBytes < 0) || (sizeInBytes > audioData.remaining())) {
+ Log.e(TAG, "AudioTrack.write() called with invalid size (" + sizeInBytes + ") value");
return ERROR_BAD_VALUE;
}
int ret = 0;
if (audioData.isDirect()) {
ret = native_write_native_bytes(audioData,
- audioData.position(),
- offsetInBytes, sizeInBytes, mAudioFormat,
+ audioData.position(), sizeInBytes, mAudioFormat,
writeMode == WRITE_BLOCKING);
} else {
ret = native_write_byte(NioUtils.unsafeArray(audioData),
- NioUtils.unsafeArrayOffset(audioData) + audioData.position() + offsetInBytes,
+ NioUtils.unsafeArrayOffset(audioData) + audioData.position(),
sizeInBytes, mAudioFormat,
writeMode == WRITE_BLOCKING);
}
@@ -1264,6 +1258,10 @@
mState = STATE_INITIALIZED;
}
+ if (ret > 0) {
+ audioData.position(audioData.position() + ret);
+ }
+
return ret;
}
@@ -1476,7 +1474,7 @@
int offsetInShorts, int sizeInShorts, int format);
private native final int native_write_native_bytes(Object audioData,
- int positionInBytes, int offsetInBytes, int sizeInBytes, int format, boolean blocking);
+ int positionInBytes, int sizeInBytes, int format, boolean blocking);
private native final int native_reload_static();
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
index 31e806c..3fc562c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
@@ -979,6 +979,13 @@
return;
}
+ if (mLockPatternUtils.checkVoldPassword()) {
+ if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
+ // Without this, settings is not enabled until the lock screen first appears
+ hideLocked();
+ return;
+ }
+
if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
showLocked(options);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index e1a4bb2..48a6522 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -22,6 +22,7 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.media.AudioManager;
import android.os.Vibrator;
import android.util.Log;
import android.view.Gravity;
@@ -605,7 +606,7 @@
mVibrator = (android.os.Vibrator)
mContext.getSystemService(Context.VIBRATOR_SERVICE);
}
- mVibrator.vibrate(duration);
+ mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index b096ead..09aa60f 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.media.AudioManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -207,7 +208,8 @@
Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0) {
Resources res = context.getResources();
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration));
+ vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration),
+ AudioManager.STREAM_SYSTEM);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index ed981ed..3d47cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -31,6 +32,7 @@
DisplayMetrics mDisplayMetrics;
public Rect systemInsets = new Rect();
+ public Rect displayRect = new Rect();
/** Private constructor */
private RecentsConfiguration() {}
@@ -51,10 +53,11 @@
/** Updates the state, given the specified context */
void update(Context context) {
- mDisplayMetrics = context.getResources().getDisplayMetrics();
+ Resources res = context.getResources();
+ DisplayMetrics dm = res.getDisplayMetrics();
+ mDisplayMetrics = dm;
- boolean isPortrait = context.getResources().getConfiguration().orientation ==
- Configuration.ORIENTATION_PORTRAIT;
+ displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
}
public void updateSystemInsets(Rect insets) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index c5e325e..cdbee82 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -29,45 +29,73 @@
import android.os.HandlerThread;
import android.os.UserHandle;
import android.util.LruCache;
+import android.util.Pair;
import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/** A bitmap load queue */
class TaskResourceLoadQueue {
ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
+ ConcurrentHashMap<Task.TaskKey, Boolean> mForceLoadSet =
+ new ConcurrentHashMap<Task.TaskKey, Boolean>();
- Task nextTask() {
- Console.log(Constants.DebugFlags.App.TaskDataLoader, " [TaskResourceLoadQueue|nextTask]");
- return mQueue.poll();
- }
+ static final Boolean sFalse = new Boolean(false);
- void addTask(Task t) {
+ /** Adds a new task to the load queue */
+ void addTask(Task t, boolean forceLoad) {
Console.log(Constants.DebugFlags.App.TaskDataLoader, " [TaskResourceLoadQueue|addTask]");
if (!mQueue.contains(t)) {
mQueue.add(t);
}
+ if (forceLoad) {
+ mForceLoadSet.put(t.key, new Boolean(true));
+ }
synchronized(this) {
notifyAll();
}
}
+ /**
+ * Retrieves the next task from the load queue, as well as whether we want that task to be
+ * force reloaded.
+ */
+ Pair<Task, Boolean> nextTask() {
+ Console.log(Constants.DebugFlags.App.TaskDataLoader, " [TaskResourceLoadQueue|nextTask]");
+ Task task = mQueue.poll();
+ Boolean forceLoadTask = null;
+ if (task != null) {
+ forceLoadTask = mForceLoadSet.remove(task.key);
+ }
+ if (forceLoadTask == null) {
+ forceLoadTask = sFalse;
+ }
+ return new Pair<Task, Boolean>(task, forceLoadTask);
+ }
+
+ /** Removes a task from the load queue */
void removeTask(Task t) {
Console.log(Constants.DebugFlags.App.TaskDataLoader, " [TaskResourceLoadQueue|removeTask]");
mQueue.remove(t);
+ mForceLoadSet.remove(t.key);
}
+ /** Clears all the tasks from the load queue */
void clearTasks() {
Console.log(Constants.DebugFlags.App.TaskDataLoader, " [TaskResourceLoadQueue|clearTasks]");
mQueue.clear();
+ mForceLoadSet.clear();
}
+ /** Returns whether the load queue is empty */
boolean isEmpty() {
return mQueue.isEmpty();
}
@@ -147,16 +175,19 @@
}
} else {
// Load the next item from the queue
- final Task t = mLoadQueue.nextTask();
+ Pair<Task, Boolean> nextTaskData = mLoadQueue.nextTask();
+ final Task t = nextTaskData.first;
+ final boolean forceLoadTask = nextTaskData.second;
if (t != null) {
try {
Drawable loadIcon = mIconCache.get(t.key);
Bitmap loadThumbnail = mThumbnailCache.get(t.key);
Console.log(Constants.DebugFlags.App.TaskDataLoader,
" [TaskResourceLoader|load]",
- t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail);
+ t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail +
+ " forceLoad: " + forceLoadTask);
// Load the icon
- if (loadIcon == null) {
+ if (loadIcon == null || forceLoadTask) {
PackageManager pm = mContext.getPackageManager();
ActivityInfo info = pm.getActivityInfo(t.key.intent.getComponent(),
PackageManager.GET_META_DATA);
@@ -172,7 +203,7 @@
}
}
// Load the thumbnail
- if (loadThumbnail == null) {
+ if (loadThumbnail == null || forceLoadTask) {
ActivityManager am = (ActivityManager)
mContext.getSystemService(Context.ACTIVITY_SERVICE);
Bitmap thumbnail = am.getTaskTopThumbnail(t.key.id);
@@ -197,7 +228,7 @@
mMainThreadHandler.post(new Runnable() {
@Override
public void run() {
- t.notifyTaskDataLoaded(newThumbnail, newIcon);
+ t.notifyTaskDataLoaded(newThumbnail, newIcon, forceLoadTask);
}
});
}
@@ -329,9 +360,11 @@
/** Reload the set of recent tasks */
SpaceNode reload(Context context, int preloadCount) {
Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
+ ArrayList<Task> tasksToForceLoad = new ArrayList<Task>();
TaskStack stack = new TaskStack(context);
SpaceNode root = new SpaceNode(context);
root.setStack(stack);
+
try {
long t1 = System.currentTimeMillis();
@@ -387,6 +420,12 @@
// Load the icon (if possible and not the foremost task, from the cache)
if (!isForemostTask) {
task.icon = mIconCache.get(task.key);
+
+ if (task.icon != null) {
+ // Even though we get things from the cache, we should update them if
+ // they've changed in the bg
+ tasksToForceLoad.add(task);
+ }
}
if (task.icon == null) {
task.icon = info.loadIcon(pm);
@@ -400,6 +439,12 @@
// Load the thumbnail (if possible and not the foremost task, from the cache)
if (!isForemostTask) {
task.thumbnail = mThumbnailCache.get(task.key);
+
+ if (task.thumbnail != null) {
+ // Even though we get things from the cache, we should update them if
+ // they've changed in the bg
+ tasksToForceLoad.add(task);
+ }
}
if (task.thumbnail == null) {
Console.log(Constants.DebugFlags.App.TaskDataLoader,
@@ -451,6 +496,11 @@
// Start the task loader
mLoader.start(context);
+ // Add all the tasks that we are force/re-loading
+ for (Task t : tasksToForceLoad) {
+ mLoadQueue.addTask(t, true);
+ }
+
return root;
}
@@ -473,9 +523,9 @@
requiresLoad = true;
}
if (requiresLoad) {
- mLoadQueue.addTask(t);
+ mLoadQueue.addTask(t, false);
}
- t.notifyTaskDataLoaded(thumbnail, icon);
+ t.notifyTaskDataLoaded(thumbnail, icon, false);
}
/** Releases the task resource data back into the pool. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 0c3c528..edcf9b2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -29,7 +29,7 @@
/* Task callbacks */
public interface TaskCallbacks {
/* Notifies when a task has been bound */
- public void onTaskDataLoaded();
+ public void onTaskDataLoaded(boolean reloadingTaskData);
/* Notifies when a task has been unbound */
public void onTaskDataUnloaded();
}
@@ -84,11 +84,11 @@
}
/** Notifies the callback listeners that this task has been loaded */
- public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable icon) {
+ public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable icon, boolean reloadingTaskData) {
this.icon = icon;
this.thumbnail = thumbnail;
if (mCb != null) {
- mCb.onTaskDataLoaded();
+ mCb.onTaskDataLoaded(reloadingTaskData);
}
}
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 77b78f3..d997222 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents.views;
import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -210,11 +211,14 @@
int offsetX = 0;
int offsetY = 0;
if (tv == null) {
- // Launch the activity
+ // If there is no actual task view, then use the stack view as the source view
+ // and then offset to the expected transform rect, but bound this to just
+ // outside the display rect (to ensure we don't animate from too far away)
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
sourceView = stackView;
transform = stackView.getStackTransform(stack.indexOfTask(task));
offsetX = transform.rect.left;
- offsetY = transform.rect.top;
+ offsetY = Math.min(transform.rect.top, config.displayRect.height());
} else {
transform = stackView.getStackTransform(stack.indexOfTask(task));
}
@@ -242,10 +246,14 @@
i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
| Intent.FLAG_ACTIVITY_TASK_ON_HOME
| Intent.FLAG_ACTIVITY_NEW_TASK);
- if (opts != null) {
- getContext().startActivityAsUser(i, opts.toBundle(), UserHandle.CURRENT);
- } else {
- getContext().startActivityAsUser(i, UserHandle.CURRENT);
+ try {
+ if (opts != null) {
+ getContext().startActivityAsUser(i, opts.toBundle(), UserHandle.CURRENT);
+ } else {
+ getContext().startActivityAsUser(i, UserHandle.CURRENT);
+ }
+ } catch (ActivityNotFoundException anfe) {
+ Console.logError(getContext(), "Could not start Activity");
}
Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsLaunchTask,
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 62cf394..2b27a06 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -881,6 +881,13 @@
}
break;
}
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ final int index = ev.getActionIndex();
+ mActivePointerId = ev.getPointerId(index);
+ mLastMotionX = (int) ev.getX(index);
+ mLastMotionY = (int) ev.getY(index);
+ break;
+ }
case MotionEvent.ACTION_MOVE: {
if (mActivePointerId == INACTIVE_POINTER_ID) break;
@@ -957,6 +964,19 @@
recycleVelocityTracker();
break;
}
+ case MotionEvent.ACTION_POINTER_UP: {
+ int pointerIndex = ev.getActionIndex();
+ int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // Select a new active pointer id and reset the motion state
+ final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ mLastMotionX = (int) ev.getX(newPointerIndex);
+ mLastMotionY = (int) ev.getY(newPointerIndex);
+ mVelocityTracker.clear();
+ }
+ break;
+ }
case MotionEvent.ACTION_CANCEL: {
if (mIsScrolling) {
// Disable HW layers
@@ -1006,10 +1026,6 @@
Task task = tv.getTask();
Activity activity = (Activity) mSv.getContext();
- // We have to disable the listener to ensure that we
- // don't hit this again
- tv.animate().setListener(null);
-
// Remove the task from the view
mSv.mStack.removeTask(task);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index f411717..e6dce37 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -65,10 +65,7 @@
setImageBitmap(t.thumbnail);
if (animate) {
- setAlpha(0f);
- animate().alpha(1f)
- .setDuration(Constants.Values.TaskView.Animation.TaskDataUpdatedFadeDuration)
- .start();
+ // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
}
}
}
@@ -143,10 +140,7 @@
if (t.icon != null) {
setImageDrawable(t.icon);
if (animate) {
- setAlpha(0f);
- animate().alpha(1f)
- .setDuration(Constants.Values.TaskView.Animation.TaskDataUpdatedFadeDuration)
- .start();
+ // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
}
}
}
@@ -311,6 +305,7 @@
.translationY(0)
.setStartDelay(235)
.setDuration(Constants.Values.TaskView.Animation.TaskIconOnEnterDuration)
+ .withLayer()
.start();
}
}
@@ -333,13 +328,8 @@
.setStartDelay(0)
.setDuration(Constants.Values.TaskView.Animation.TaskIconOnLeavingDuration)
.setInterpolator(new DecelerateInterpolator())
- .setListener(
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- r.run();
- }
- })
+ .withLayer()
+ .withEndAction(r)
.start();
}
}
@@ -379,10 +369,10 @@
}
@Override
- public void onTaskDataLoaded() {
+ public void onTaskDataLoaded(boolean reloadingTaskData) {
// Bind each of the views to the new task data
- mThumbnailView.rebindToTask(mTask, false);
- mIconView.rebindToTask(mTask, false);
+ mThumbnailView.rebindToTask(mTask, reloadingTaskData);
+ mIconView.rebindToTask(mTask, reloadingTaskData);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index bd36128..68c8364 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -111,7 +111,7 @@
protected static final boolean ENABLE_HEADS_UP = true;
// scores above this threshold should be displayed in heads up mode.
- protected static final int INTERRUPTION_THRESHOLD = 11;
+ protected static final int INTERRUPTION_THRESHOLD = 10;
protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
// Should match the value in PhoneWindowManager
@@ -757,6 +757,7 @@
protected void toggleRecentsActivity() {
if (mRecents != null) {
+ sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
}
}
@@ -1521,17 +1522,8 @@
// A: Almost none! Only things coming from the system (package is "android") that also
// have special "kind" tags marking them as relevant for setup (see below).
protected boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
- if ("android".equals(sbn.getPackageName())) {
- if (sbn.getNotification().kind != null) {
- for (String aKind : sbn.getNotification().kind) {
- // IME switcher, created by InputMethodManagerService
- if ("android.system.imeswitcher".equals(aKind)) return true;
- // OTA availability & errors, created by SystemUpdateService
- if ("android.system.update".equals(aKind)) return true;
- }
- }
- }
- return false;
+ return "android".equals(sbn.getPackageName())
+ && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
}
public boolean inKeyguardRestrictedInputMode() {
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 9540bd4..91325ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -49,6 +49,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
+import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -2713,7 +2714,7 @@
void vibrate() {
android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
Context.VIBRATOR_SERVICE);
- vib.vibrate(250);
+ vib.vibrate(250, AudioManager.STREAM_SYSTEM);
}
Runnable mStartTracing = new Runnable() {
diff --git a/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml b/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml
index 1622742..2a0188a 100644
--- a/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml
+++ b/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml
@@ -20,6 +20,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="?android:actionButtonStyle"
+ android:id="@+id/set_wallpaper_button"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView style="?android:actionBarTabTextStyle"
diff --git a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
index cdc5cdc..764156d 100644
--- a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
+++ b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
@@ -245,6 +245,9 @@
try {
ei.readExif(mPath);
return true;
+ } catch (NullPointerException e) {
+ Log.w("BitmapRegionTileSource", "reading exif failed", e);
+ return false;
} catch (IOException e) {
Log.w("BitmapRegionTileSource", "getting decoder failed", e);
return false;
@@ -311,6 +314,9 @@
} catch (IOException e) {
Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
return false;
+ } catch (NullPointerException e) {
+ Log.e("BitmapRegionTileSource", "Failed to read EXIF for URI " + mUri, e);
+ return false;
} finally {
Utils.closeSilently(is);
}
diff --git a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
index e45b98c..d6c0c99 100644
--- a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
+++ b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
@@ -75,6 +75,7 @@
protected CropView mCropView;
protected Uri mUri;
+ private View mSetWallpaperButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -111,10 +112,12 @@
cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
}
});
+ mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
// Load image in background
final BitmapRegionTileSource.UriBitmapSource bitmapSource =
new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024);
+ mSetWallpaperButton.setVisibility(View.INVISIBLE);
Runnable onLoad = new Runnable() {
public void run() {
if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
@@ -122,6 +125,8 @@
getString(R.string.wallpaper_load_fail),
Toast.LENGTH_LONG).show();
finish();
+ } else {
+ mSetWallpaperButton.setVisibility(View.VISIBLE);
}
}
};
@@ -136,7 +141,21 @@
final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
protected Void doInBackground(Void...args) {
if (!isCancelled()) {
- bitmapSource.loadInBackground();
+ try {
+ bitmapSource.loadInBackground();
+ } catch (SecurityException securityException) {
+ if (isDestroyed()) {
+ // Temporarily granted permissions are revoked when the activity
+ // finishes, potentially resulting in a SecurityException here.
+ // Even though {@link #isDestroyed} might also return true in different
+ // situations where the configuration changes, we are fine with
+ // catching these cases here as well.
+ cancel(false);
+ } else {
+ // otherwise it had a different cause and we throw it further
+ throw securityException;
+ }
+ }
}
return null;
}
@@ -271,6 +290,9 @@
}
} catch (IOException e) {
Log.w(LOGTAG, "Getting exif data failed", e);
+ } catch (NullPointerException e) {
+ // Sometimes the ExifInterface has an internal NPE if Exif data isn't valid
+ Log.w(LOGTAG, "Getting exif data failed", e);
} finally {
Utils.closeSilently(bis);
Utils.closeSilently(is);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 3c23c6e..41705ef 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -106,6 +106,7 @@
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.HashSet;
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
@@ -374,7 +375,7 @@
static final Rect mTmpNavigationFrame = new Rect();
WindowState mTopFullscreenOpaqueWindowState;
- boolean mHideWindowBehindKeyguard;
+ HashSet<Integer> mTasksToBeHidden = new HashSet<Integer>();
boolean mTopIsFullscreen;
boolean mForceStatusBar;
boolean mForceStatusBarFromKeyguard;
@@ -3366,7 +3367,7 @@
@Override
public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
mTopFullscreenOpaqueWindowState = null;
- mHideWindowBehindKeyguard = false;
+ mTasksToBeHidden.clear();
mForceStatusBar = false;
mForceStatusBarFromKeyguard = false;
mForcingShowNavBar = false;
@@ -3417,12 +3418,18 @@
final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0;
if (appWindow) {
+ final int taskId = win.getTaskId();
+ if (taskId != WindowState.UNKNOWN_TASK_ID && showWhenLocked) {
+ mTasksToBeHidden.remove(taskId);
+ } else {
+ mTasksToBeHidden.add(taskId);
+ }
if (attrs.x == 0 && attrs.y == 0
&& attrs.width == WindowManager.LayoutParams.MATCH_PARENT
&& attrs.height == WindowManager.LayoutParams.MATCH_PARENT) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
mTopFullscreenOpaqueWindowState = win;
- if (!mHideWindowBehindKeyguard) {
+ if (mTasksToBeHidden.isEmpty()) {
if (showWhenLocked) {
if (DEBUG_LAYOUT) Slog.v(TAG,
"Setting mHideLockScreen to true by win " + win);
@@ -3442,8 +3449,6 @@
if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
mAllowLockscreenWhenOn = true;
}
- } else if (!showWhenLocked) {
- mHideWindowBehindKeyguard = true;
}
}
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index af8a6e7..31822e7 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -637,7 +637,8 @@
mImeSwitcherNotification.vibrate = null;
// Tag this notification specially so SystemUI knows it's important
- mImeSwitcherNotification.kind = new String[] { "android.system.imeswitcher" };
+ mImeSwitcherNotification.extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
+ mImeSwitcherNotification.category = Notification.CATEGORY_SYSTEM;
Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index fe814fc..19e8083 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -30,7 +30,11 @@
import android.database.sqlite.SQLiteStatement;
import android.os.Binder;
import android.os.Environment;
+import android.os.IBinder;
import android.os.RemoteException;
+import android.os.storage.IMountService;
+import android.os.ServiceManager;
+import android.os.storage.StorageManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -79,6 +83,7 @@
private final Context mContext;
private LockPatternUtils mLockPatternUtils;
+ private boolean mFirstCallToVold;
public LockSettingsService(Context context) {
mContext = context;
@@ -86,6 +91,7 @@
mOpenHelper = new DatabaseHelper(mContext);
mLockPatternUtils = new LockPatternUtils(context);
+ mFirstCallToVold = true;
}
public void systemReady() {
@@ -347,6 +353,33 @@
}
@Override
+ public boolean checkVoldPassword(int userId) throws RemoteException {
+ if (!mFirstCallToVold) {
+ return false;
+ }
+ mFirstCallToVold = false;
+
+ checkPasswordReadPermission(userId);
+
+ // There's no guarantee that this will safely connect, but if it fails
+ // we will simply show the lock screen when we shouldn't, so relatively
+ // benign. There is an outside chance something nasty would happen if
+ // this service restarted before vold stales out the password in this
+ // case. The nastiness is limited to not showing the lock screen when
+ // we should, within the first minute of decrypting the phone if this
+ // service can't connect to vold, it restarts, and then the new instance
+ // does successfully connect.
+ final IMountService service = getMountService();
+ String password = service.getPassword();
+ service.clearPassword();
+ if (service.getPasswordType() == StorageManager.CRYPT_TYPE_PATTERN) {
+ return checkPattern(password, userId);
+ } else {
+ return checkPassword(password, userId);
+ }
+ }
+
+ @Override
public void removeUser(int userId) {
checkWritePermission(userId);
@@ -524,4 +557,12 @@
Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
Secure.LOCK_SCREEN_OWNER_INFO
};
+
+ private IMountService getMountService() {
+ final IBinder service = ServiceManager.getService("mount");
+ if (service != null) {
+ return IMountService.Stub.asInterface(service);
+ }
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index cd74fed..b6e0d5f 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -74,6 +74,7 @@
import com.google.android.collect.Maps;
import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.codec.DecoderException;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
@@ -573,6 +574,14 @@
}
}
+ private boolean isReady() {
+ try {
+ return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+
private void handleSystemReady() {
// Snapshot current volume states since it's not safe to call into vold
// while holding locks.
@@ -2081,6 +2090,19 @@
return new String(Hex.encodeHex(bytes));
}
+ private String fromHex(String hexPassword) {
+ if (hexPassword == null) {
+ return null;
+ }
+
+ try {
+ byte[] bytes = Hex.decodeHex(hexPassword.toCharArray());
+ return new String(bytes, StandardCharsets.UTF_8);
+ } catch (DecoderException e) {
+ return null;
+ }
+ }
+
@Override
public int decryptStorage(String password) {
if (TextUtils.isEmpty(password)) {
@@ -2230,6 +2252,35 @@
}
@Override
+ public String getPassword() throws RemoteException {
+ if (!isReady()) {
+ return new String();
+ }
+
+ final NativeDaemonEvent event;
+ try {
+ event = mConnector.execute("cryptfs", "getpw");
+ return fromHex(event.getMessage());
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void clearPassword() throws RemoteException {
+ if (!isReady()) {
+ return;
+ }
+
+ final NativeDaemonEvent event;
+ try {
+ event = mConnector.execute("cryptfs", "clearpw");
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
public int mkdirs(String callingPkg, String appPath) {
final int userId = UserHandle.getUserId(Binder.getCallingUid());
final UserEnvironment userEnv = new UserEnvironment(userId);
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 52f9aa9..aa756a1 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -362,7 +362,7 @@
} catch (RemoteException e) {
}
if (vib.mTimeout != 0) {
- doVibratorOn(vib.mTimeout, vib.mUid);
+ doVibratorOn(vib.mTimeout, vib.mUid, vib.mStreamHint);
mH.postDelayed(mVibrationRunnable, vib.mTimeout);
} else {
// mThread better be null here. doCancelVibrate should always be
@@ -481,7 +481,7 @@
return vibratorExists();
}
- private void doVibratorOn(long millis, int uid) {
+ private void doVibratorOn(long millis, int uid, int streamHint) {
synchronized (mInputDeviceVibrators) {
try {
mBatteryStatsService.noteVibratorOn(uid, millis);
@@ -491,7 +491,7 @@
final int vibratorCount = mInputDeviceVibrators.size();
if (vibratorCount != 0) {
for (int i = 0; i < vibratorCount; i++) {
- mInputDeviceVibrators.get(i).vibrate(millis);
+ mInputDeviceVibrators.get(i).vibrate(millis, streamHint);
}
} else {
vibratorOn(millis);
@@ -554,6 +554,7 @@
final int len = pattern.length;
final int repeat = mVibration.mRepeat;
final int uid = mVibration.mUid;
+ final int streamHint = mVibration.mStreamHint;
int index = 0;
long duration = 0;
@@ -574,7 +575,7 @@
// duration is saved for delay() at top of loop
duration = pattern[index++];
if (duration > 0) {
- VibratorService.this.doVibratorOn(duration, uid);
+ VibratorService.this.doVibratorOn(duration, uid, streamHint);
}
} else {
if (repeat < 0) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index e43dea9..026fd296 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2450,7 +2450,7 @@
Log.v(TAG, "canceling and rescheduling sync since an initialization "
+ "takes higher priority, " + conflict);
}
- } else if (candidate.expedited && !conflict.mSyncOperation.expedited
+ } else if (candidate.isExpedited() && !conflict.mSyncOperation.isExpedited()
&& (candidateIsInitialization
== conflict.mSyncOperation.isInitialization())) {
toReschedule = conflict;
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 5233014..9a4abce 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -65,17 +65,18 @@
/** Why this sync was kicked off. {@link #REASON_NAMES} */
public final int reason;
/** Where this sync was initiated. */
- public int syncSource;
+ public final int syncSource;
public final boolean allowParallelSyncs;
- public Bundle extras;
public final String key;
- public boolean expedited;
+ /** Internal boolean to avoid reading a bundle everytime we want to compare operations. */
+ private final boolean expedited;
+ public Bundle extras;
/** Bare-bones version of this operation that is persisted across reboots. */
public SyncStorageEngine.PendingOperation pendingOperation;
/** Elapsed real time in millis at which to run this sync. */
public long latestRunTime;
/** Set by the SyncManager in order to delay retries. */
- public Long backoff;
+ public long backoff;
/** Specified by the adapter to delay subsequent sync operations. */
public long delayUntil;
/**
@@ -92,61 +93,58 @@
public SyncOperation(Account account, int userId, int reason, int source, String provider,
Bundle extras, long runTimeFromNow, long flexTime, long backoff,
long delayUntil, boolean allowParallelSyncs) {
- this.target = new SyncStorageEngine.EndPoint(account, provider, userId);
- this.reason = reason;
- this.allowParallelSyncs = allowParallelSyncs;
- this.key = initialiseOperation(this.target, source, extras, runTimeFromNow, flexTime,
- backoff, delayUntil);
+ this(new SyncStorageEngine.EndPoint(account, provider, userId),
+ reason, source, extras, runTimeFromNow, flexTime, backoff, delayUntil,
+ allowParallelSyncs);
}
public SyncOperation(ComponentName service, int userId, int reason, int source,
Bundle extras, long runTimeFromNow, long flexTime, long backoff,
long delayUntil) {
- this.target = new SyncStorageEngine.EndPoint(service, userId);
- // Default to true for sync service. The service itself decides how to handle this.
- this.allowParallelSyncs = true;
+ this(new SyncStorageEngine.EndPoint(service, userId), reason, source, extras,
+ runTimeFromNow, flexTime, backoff, delayUntil, true /* allowParallelSyncs */);
+ }
+
+ private SyncOperation(SyncStorageEngine.EndPoint info, int reason, int source, Bundle extras,
+ long runTimeFromNow, long flexTime, long backoff, long delayUntil,
+ boolean allowParallelSyncs) {
+ this.target = info;
this.reason = reason;
- this.key =
- initialiseOperation(this.target,
- source, extras, runTimeFromNow, flexTime, backoff, delayUntil);
- }
-
- /** Used to reschedule a sync at a new point in time. */
- SyncOperation(SyncOperation other, long newRunTimeFromNow) {
- this.target = other.target;
- this.reason = other.reason;
- this.expedited = other.expedited;
- this.allowParallelSyncs = other.allowParallelSyncs;
- // re-use old flex, but only
- long newFlexTime = Math.min(other.flexTime, newRunTimeFromNow);
- this.key =
- initialiseOperation(this.target,
- other.syncSource, other.extras,
- newRunTimeFromNow /* runTimeFromNow*/,
- newFlexTime /* flexTime */,
- other.backoff,
- 0L /* delayUntil */);
- }
-
- private String initialiseOperation(SyncStorageEngine.EndPoint info, int source, Bundle extras,
- long runTimeFromNow, long flexTime, long backoff, long delayUntil) {
this.syncSource = source;
this.extras = new Bundle(extras);
cleanBundle(this.extras);
this.delayUntil = delayUntil;
this.backoff = backoff;
+ this.allowParallelSyncs = allowParallelSyncs;
final long now = SystemClock.elapsedRealtime();
- if (runTimeFromNow < 0 || isExpedited()) {
+ // Set expedited based on runTimeFromNow. The SyncManager specifies whether the op is
+ // expedited (Not done solely based on bundle).
+ if (runTimeFromNow < 0) {
this.expedited = true;
+ // Sanity check: Will always be true.
+ if (!this.extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
+ this.extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+ }
this.latestRunTime = now;
this.flexTime = 0;
} else {
this.expedited = false;
+ this.extras.remove(ContentResolver.SYNC_EXTRAS_EXPEDITED);
this.latestRunTime = now + runTimeFromNow;
this.flexTime = flexTime;
}
updateEffectiveRunTime();
- return toKey(info, this.extras);
+ this.key = toKey(info, this.extras);
+ }
+
+ /** Used to reschedule a sync at a new point in time. */
+ public SyncOperation(SyncOperation other, long newRunTimeFromNow) {
+ this(other.target, other.reason, other.syncSource, new Bundle(other.extras),
+ newRunTimeFromNow,
+ 0L /* In back-off so no flex */,
+ other.backoff,
+ other.delayUntil,
+ other.allowParallelSyncs);
}
public boolean matchesAuthority(SyncOperation other) {
@@ -264,7 +262,7 @@
}
public boolean isExpedited() {
- return extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false) || expedited;
+ return expedited;
}
public boolean ignoreBackoff() {
diff --git a/services/core/java/com/android/server/content/SyncQueue.java b/services/core/java/com/android/server/content/SyncQueue.java
index 5d93882..587de1c 100644
--- a/services/core/java/com/android/server/content/SyncQueue.java
+++ b/services/core/java/com/android/server/content/SyncQueue.java
@@ -76,12 +76,11 @@
operationToAdd = new SyncOperation(
info.account, info.userId, op.reason, op.syncSource, info.provider,
op.extras,
- 0 /* delay */,
+ op.expedited ? -1 : 0 /* delay */,
0 /* flex */,
- backoff != null ? backoff.first : 0,
+ backoff != null ? backoff.first : 0L,
mSyncStorageEngine.getDelayUntilTime(info),
syncAdapterInfo.type.allowParallelSyncs());
- operationToAdd.expedited = op.expedited;
operationToAdd.pendingOperation = op;
add(operationToAdd, op);
} else if (info.target_service) {
@@ -96,11 +95,10 @@
operationToAdd = new SyncOperation(
info.service, info.userId, op.reason, op.syncSource,
op.extras,
- 0 /* delay */,
+ op.expedited ? -1 : 0 /* delay */,
0 /* flex */,
backoff != null ? backoff.first : 0,
mSyncStorageEngine.getDelayUntilTime(info));
- operationToAdd.expedited = op.expedited;
operationToAdd.pendingOperation = op;
add(operationToAdd, op);
}
@@ -129,7 +127,6 @@
if (existingOperation != null) {
boolean changed = false;
if (operation.compareTo(existingOperation) <= 0 ) {
- existingOperation.expedited = operation.expedited;
long newRunTime =
Math.min(existingOperation.latestRunTime, operation.latestRunTime);
// Take smaller runtime.
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 5df7fc6..3a2e4db 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -1088,7 +1088,7 @@
}
pop = new PendingOperation(authority, op.reason, op.syncSource, op.extras,
- op.expedited);
+ op.isExpedited());
mPendingOperations.add(pop);
appendPendingOperationLocked(pop);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
new file mode 100644
index 0000000..9916435
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
@@ -0,0 +1,170 @@
+/*
+ * 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 android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiCecListener;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CecDevice class represents a CEC logical device characterized
+ * by its device type. A physical device can contain the functions of
+ * more than one logical device, in which case it should create
+ * as many logical devices as necessary.
+ *
+ * <p>Note that if a physical device has multiple instances of a particular
+ * functionality, it should advertize only one instance. For instance, if
+ * a device has multiple tuners, it should only expose one for control
+ * via CEC. In this case, it is up to the device itself to manage multiple tuners.
+ *
+ * <p>The version of HDMI-CEC protocol supported in this class is 1.3a.
+ *
+ * <p>Declared as package-private, accessed by HdmiCecService only.
+ */
+final class HdmiCecDevice {
+ private static final String TAG = "HdmiCecDevice";
+
+ private final int mType;
+
+ // List of listeners to the message/event coming to the device.
+ private final List<IHdmiCecListener> mListeners = new ArrayList<IHdmiCecListener>();
+ private final Binder mBinder = new Binder();
+
+ private String mName;
+ private boolean mIsActiveSource;
+
+ /**
+ * Constructor.
+ */
+ public HdmiCecDevice(int type) {
+ mType = type;
+ mIsActiveSource = false;
+ }
+
+ /**
+ * Return the binder token that identifies this instance.
+ */
+ public Binder getToken() {
+ return mBinder;
+ }
+
+ /**
+ * Return the type of this device.
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Set the name of the device. The name will be transferred via the message
+ * <Set OSD Name> to other HDMI-CEC devices connected through HDMI
+ * cables and shown on TV screen to identify the devicie.
+ *
+ * @param name name of the device
+ */
+ public void setName(String name) {
+ mName = name;
+ }
+
+ /**
+ * Return the name of this device.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Register a listener to be invoked when events occur.
+ *
+ * @param listener the listern that will run
+ */
+ public void addListener(IHdmiCecListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Remove the listener that was previously registered.
+ *
+ * @param listener IHdmiCecListener instance to be removed
+ */
+ public void removeListener(IHdmiCecListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Indicate if the device has listeners.
+ *
+ * @return true if there are listener instances for this device
+ */
+ public boolean hasListener() {
+ return !mListeners.isEmpty();
+ }
+
+ /**
+ * Handle HDMI-CEC message coming to the device by invoking the registered
+ * listeners.
+ */
+ public void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
+ if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE) {
+ mIsActiveSource = false;
+ }
+ if (mListeners.size() == 0) {
+ return;
+ }
+ HdmiCecMessage message = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
+ for (IHdmiCecListener listener : mListeners) {
+ try {
+ listener.onMessageReceived(message);
+ } catch (RemoteException e) {
+ Log.e(TAG, "listener.onMessageReceived failed.");
+ }
+ }
+ }
+
+ public void handleHotplug(boolean connected) {
+ for (IHdmiCecListener listener : mListeners) {
+ try {
+ listener.onCableStatusChanged(connected);
+ } catch (RemoteException e) {
+ Log.e(TAG, "listener.onCableStatusChanged failed.");
+ }
+ }
+ }
+
+ /**
+ * Return the active status of the device.
+ *
+ * @return true if the device is the active source among the connected
+ * HDMI-CEC-enabled devices; otherwise false.
+ */
+ public boolean isActiveSource() {
+ return mIsActiveSource;
+ }
+
+ /**
+ * Update the active source state of the device.
+ */
+ public void setIsActiveSource(boolean state) {
+ mIsActiveSource = state;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecService.java b/services/core/java/com/android/server/hdmi/HdmiCecService.java
new file mode 100644
index 0000000..01f2ec3
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecService.java
@@ -0,0 +1,394 @@
+/*
+ * 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 android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiCecListener;
+import android.hardware.hdmi.IHdmiCecService;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * Provides a service for sending and processing HDMI-CEC messages, and providing
+ * the information on HDMI settings in general.
+ */
+public final class HdmiCecService extends SystemService {
+ private static final String TAG = "HdmiCecService";
+
+ // Maintains the allocated logical devices. Device type, not logical address,
+ // is used for key as logical address is likely to change over time while
+ // device type is permanent. Type-address mapping is maintained only at
+ // native level.
+ private final SparseArray<HdmiCecDevice> mLogicalDevices = new SparseArray<HdmiCecDevice>();
+
+ // List of IBinder.DeathRecipient instances to handle dead IHdmiCecListener
+ // objects.
+ private final ArrayList<ListenerRecord> mListenerRecords = new ArrayList<ListenerRecord>();
+
+ // Used to synchronize the access to the service.
+ private final Object mLock = new Object();
+
+ // Stores the pointer to the native implementation of the service that
+ // interacts with HAL.
+ private long mNativePtr;
+
+ private static final String PERMISSION = "android.permission.HDMI_CEC";
+
+ // Service name under which it is registered to service manager.
+ // TODO: Move this to Context once HdmiCecManager is introduced.
+ private static final String HDMI_CEC_SERVICE = "hdmi_cec";
+
+ public HdmiCecService(Context context) {
+ super(context);
+ }
+
+ private static native long nativeInit(HdmiCecService service);
+
+ @Override
+ public void onStart() {
+ mNativePtr = nativeInit(this);
+ publishBinderService(HDMI_CEC_SERVICE, new BinderService());
+ }
+
+ /**
+ * Called by native when an HDMI-CEC message arrived. Invokes the registered
+ * listeners to handle the message.
+ */
+ private void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
+ // TODO: Messages like <Standby> may not need be passed to listener
+ // but better be handled in service by turning off the screen
+ // or putting the device into suspend mode. List up such messages
+ // and handle them here.
+ int type = HdmiCec.getTypeFromAddress(dstAddress);
+ synchronized (mLock) {
+ if (dstAddress == HdmiCec.ADDR_BROADCAST) {
+ for (int i = 0; i < mLogicalDevices.size(); ++i) {
+ mLogicalDevices.valueAt(i).handleMessage(srcAddress, dstAddress, opcode,
+ params);
+ }
+ } else {
+ HdmiCecDevice device = mLogicalDevices.get(type);
+ if (device == null) {
+ Log.w(TAG, "logical device not found. type: " + type);
+ return;
+ }
+ device.handleMessage(srcAddress, dstAddress, opcode, params);
+ }
+ }
+ }
+
+ /**
+ * Called by native when internal HDMI hotplug event occurs. Invokes the registered
+ * listeners to handle the event.
+ */
+ private void handleHotplug(boolean connected) {
+ synchronized(mLock) {
+ for (int i = 0; i < mLogicalDevices.size(); ++i) {
+ mLogicalDevices.valueAt(i).handleHotplug(connected);
+ }
+ }
+ }
+
+ /**
+ * Called by native when it needs to know whether we have an active source.
+ * The native part uses the return value to respond to <Request Active
+ * Source >.
+ *
+ * @return type of the device which is active; DEVICE_INACTIVE if there is
+ * no active logical device in the system.
+ */
+ private int getActiveSource() {
+ synchronized(mLock) {
+ for (int i = 0; i < mLogicalDevices.size(); ++i) {
+ if (mLogicalDevices.valueAt(i).isActiveSource()) {
+ return mLogicalDevices.keyAt(i);
+ }
+ }
+ }
+ return HdmiCec.DEVICE_INACTIVE;
+ }
+
+ /**
+ * Called by native when a request for the device OSD name was received.
+ * The native part uses the return value to generate the message
+ * <Set Osd Name> in response.
+ */
+ private byte[] getOsdName(int type) {
+ synchronized (mLock) {
+ HdmiCecDevice device = mLogicalDevices.get(type);
+ if (device != null) {
+ return device.getName().getBytes(Charset.forName("US-ASCII"));
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Called by native when a request for the menu language of the device was
+ * received. The native part uses the return value to generate the message
+ * <Set Menu Language> in response. The language should be of
+ * the 3-letter format as defined in ISO/FDIS 639-2. We use system default
+ * locale.
+ */
+ private String getLanguage(int type) {
+ return Locale.getDefault().getISO3Language();
+ }
+
+ private void enforceAccessPermission() {
+ getContext().enforceCallingOrSelfPermission(PERMISSION, "HdmiCecService");
+ }
+
+ private void dumpInternal(PrintWriter pw) {
+ pw.println("HdmiCecService (dumpsys hdmi_cec)");
+ pw.println("");
+ synchronized (mLock) {
+ for (int i = 0; i < mLogicalDevices.size(); ++i) {
+ HdmiCecDevice device = mLogicalDevices.valueAt(i);
+ pw.println("Device: name=" + device.getName() +
+ ", type=" + device.getType() +
+ ", active=" + device.isActiveSource());
+ }
+ }
+ }
+
+ // Remove logical device of a given type.
+ private void removeLogicalDeviceLocked(int type) {
+ ensureValidType(type);
+ mLogicalDevices.remove(type);
+ nativeRemoveLogicalAddress(mNativePtr, type);
+ }
+
+ private static void ensureValidType(int type) {
+ if (!HdmiCec.isValidType(type)) {
+ throw new IllegalArgumentException("invalid type: " + type);
+ }
+ }
+
+ // Return the logical device identified by the given binder token.
+ private HdmiCecDevice getLogicalDeviceLocked(IBinder b) {
+ for (int i = 0; i < mLogicalDevices.size(); ++i) {
+ HdmiCecDevice device = mLogicalDevices.valueAt(i);
+ if (device.getToken() == b) {
+ return device;
+ }
+ }
+ throw new IllegalArgumentException("Device not found");
+ }
+
+ private final class ListenerRecord implements IBinder.DeathRecipient {
+ private final IHdmiCecListener mListener;
+ private final int mType;
+
+ public ListenerRecord(IHdmiCecListener listener, int type) {
+ mListener = listener;
+ mType = type;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ mListenerRecords.remove(this);
+ HdmiCecDevice device = mLogicalDevices.get(mType);
+ if (device != null) {
+ device.removeListener(mListener);
+ if (!device.hasListener()) {
+ removeLogicalDeviceLocked(mType);
+ }
+ }
+ }
+ }
+ }
+
+ private final class BinderService extends IHdmiCecService.Stub {
+
+ @Override
+ public IBinder allocateLogicalDevice(int type, IHdmiCecListener listener) {
+ enforceAccessPermission();
+ ensureValidType(type);
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+ synchronized (mLock) {
+ HdmiCecDevice device = mLogicalDevices.get(type);
+ if (device != null) {
+ Log.v(TAG, "Logical address already allocated. Adding listener only.");
+ } else {
+ int address = nativeAllocateLogicalAddress(mNativePtr, type);
+ if (!HdmiCec.isValidAddress(address)) {
+ Log.e(TAG, "Logical address was not allocated");
+ return null;
+ } else {
+ device = new HdmiCecDevice(type);
+ device.setName(HdmiCec.getDefaultDeviceName(address));
+ mLogicalDevices.put(type, device);
+ }
+ }
+
+ // Adds the listener and its monitor
+ ListenerRecord record = new ListenerRecord(listener, type);
+ try {
+ listener.asBinder().linkToDeath(record, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Listener already died");
+ if (!device.hasListener()) {
+ removeLogicalDeviceLocked(type);
+ }
+ return null;
+ }
+ mListenerRecords.add(record);
+ device.addListener(listener);
+ return device.getToken();
+ }
+ }
+
+ @Override
+ public void setOsdName(IBinder b, String name) {
+ enforceAccessPermission();
+ if (TextUtils.isEmpty(name)) {
+ throw new IllegalArgumentException("name must not be null");
+ }
+ synchronized (mLock) {
+ HdmiCecDevice device = getLogicalDeviceLocked(b);
+ device.setName(name);
+ }
+ }
+
+ @Override
+ public void sendActiveSource(IBinder b) {
+ enforceAccessPermission();
+ synchronized (mLock) {
+ HdmiCecDevice device = getLogicalDeviceLocked(b);
+ device.setIsActiveSource(true);
+ nativeSendActiveSource(mNativePtr, device.getType());
+ }
+ }
+
+ @Override
+ public void sendInactiveSource(IBinder b) {
+ enforceAccessPermission();
+ synchronized (mLock) {
+ HdmiCecDevice device = getLogicalDeviceLocked(b);
+ device.setIsActiveSource(false);
+ nativeSendInactiveSource(mNativePtr, device.getType());
+ }
+ }
+
+ @Override
+ public void sendImageViewOn(IBinder b) {
+ enforceAccessPermission();
+ synchronized (mLock) {
+ HdmiCecDevice device = getLogicalDeviceLocked(b);
+ nativeSendImageViewOn(mNativePtr, device.getType());
+ }
+ }
+
+ @Override
+ public void sendTextViewOn(IBinder b) {
+ enforceAccessPermission();
+ synchronized (mLock) {
+ HdmiCecDevice device = getLogicalDeviceLocked(b);
+ nativeSendTextViewOn(mNativePtr, device.getType());
+ }
+ }
+
+ @Override
+ public void sendGiveDevicePowerStatus(IBinder b, int address) {
+ enforceAccessPermission();
+ synchronized (mLock) {
+ HdmiCecDevice device = getLogicalDeviceLocked(b);
+ nativeSendGiveDevicePowerStatus(mNativePtr, device.getType(), address);
+ }
+ }
+
+ @Override
+ public void removeServiceListener(IBinder b, IHdmiCecListener listener) {
+ enforceAccessPermission();
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+ synchronized (mLock) {
+ HdmiCecDevice device = getLogicalDeviceLocked(b);
+ for (ListenerRecord record : mListenerRecords) {
+ if (record.mType == device.getType()
+ && record.mListener.asBinder() == listener.asBinder()) {
+ mListenerRecords.remove(record);
+ device.removeListener(record.mListener);
+ if (!device.hasListener()) {
+ removeLogicalDeviceLocked(record.mType);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void sendMessage(IBinder b, HdmiCecMessage message) {
+ enforceAccessPermission();
+ if (message == null) {
+ throw new IllegalArgumentException("message must not be null");
+ }
+ synchronized (mLock) {
+ HdmiCecDevice device = getLogicalDeviceLocked(b);
+ nativeSendMessage(mNativePtr, device.getType(), message.getDestination(),
+ message.getOpcode(), message.getParams());
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission denial: can't dump HdmiCecService from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ dumpInternal(pw);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ private static native int nativeAllocateLogicalAddress(long handler, int deviceType);
+ private static native void nativeRemoveLogicalAddress(long handler, int deviceType);
+ private static native void nativeSendMessage(long handler, int deviceType, int destination,
+ int opcode, byte[] params);
+ private static native void nativeSendActiveSource(long handler, int deviceType);
+ private static native void nativeSendInactiveSource(long handler, int deviceType);
+ private static native void nativeSendImageViewOn(long handler, int deviceType);
+ private static native void nativeSendTextViewOn(long handler, int deviceType);
+ private static native void nativeSendGiveDevicePowerStatus(long handler, int deviceType,
+ int address);
+}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 88a27f5..8fed79f 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -24,6 +24,7 @@
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetoothManager;
+import android.media.AudioManager;
import android.nfc.NfcAdapter;
import android.nfc.INfcAdapter;
import android.content.BroadcastReceiver;
@@ -498,7 +499,7 @@
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator();
try {
- vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
+ vibrator.vibrate(SHUTDOWN_VIBRATE_MS, AudioManager.STREAM_SYSTEM);
} catch (Exception e) {
// Failure to vibrate shouldn't interrupt shutdown. Just log it.
Log.w(TAG, "Failed to vibrate during shutdown.", e);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 756e06a..7d8b5af 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -19,18 +19,22 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Debug;
import android.os.Handler;
import android.os.IRemoteCallback;
+import android.os.SystemProperties;
import android.util.Slog;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
+import android.view.animation.ClipRectAnimation;
import android.view.animation.Interpolator;
import android.view.animation.ScaleAnimation;
+import android.view.animation.TranslateAnimation;
import com.android.internal.util.DumpUtils.Dump;
import com.android.server.AttributeCache;
import com.android.server.wm.WindowManagerService.H;
@@ -125,6 +129,12 @@
private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4;
private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
+ // These are the possible states for the enter/exit activities during a thumbnail transition
+ private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
+ private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
+ private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
+ private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
+
private String mNextAppTransitionPackage;
private Bitmap mNextAppTransitionThumbnail;
// Used for thumbnail transitions. True if we're scaling up, false if scaling down
@@ -148,10 +158,13 @@
private final Interpolator mThumbnailFadeoutInterpolator;
private int mCurrentUserId = 0;
+ private boolean mUseAlternateThumbnailAnimation;
AppTransition(Context context, Handler h) {
mContext = context;
mH = h;
+ mUseAlternateThumbnailAnimation =
+ SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false);
mConfigShortAnimTime = context.getResources().getInteger(
com.android.internal.R.integer.config_shortAnimTime);
mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
@@ -384,80 +397,10 @@
return a;
}
- Animation createThumbnailAnimationLocked(int transit, boolean enter, boolean thumb,
- int appWidth, int appHeight) {
- Animation a;
- final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
- final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
- final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
- if (thumb) {
- // Animation for zooming thumbnail from its initial size to
- // filling the screen.
- if (mNextAppTransitionScaleUp) {
- float scaleW = appWidth / thumbWidth;
- float scaleH = appHeight / thumbHeight;
- Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mNextAppTransitionStartX, 1 / scaleW),
- computePivot(mNextAppTransitionStartY, 1 / scaleH));
- scale.setInterpolator(mDecelerateInterpolator);
-
- Animation alpha = new AlphaAnimation(1, 0);
- alpha.setInterpolator(mThumbnailFadeoutInterpolator);
-
- // This AnimationSet uses the Interpolators assigned above.
- AnimationSet set = new AnimationSet(false);
- set.addAnimation(scale);
- set.addAnimation(alpha);
- a = set;
- } else {
- float scaleW = appWidth / thumbWidth;
- float scaleH = appHeight / thumbHeight;
- a = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mNextAppTransitionStartX, 1 / scaleW),
- computePivot(mNextAppTransitionStartY, 1 / scaleH));
- }
- } else if (enter) {
- // Entering app zooms out from the center of the thumbnail.
- if (mNextAppTransitionScaleUp) {
- float scaleW = thumbWidth / appWidth;
- float scaleH = thumbHeight / appHeight;
- a = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mNextAppTransitionStartX, scaleW),
- computePivot(mNextAppTransitionStartY, scaleH));
- } else {
- // noop animation
- a = new AlphaAnimation(1, 1);
- }
- } else {
- // Exiting app
- if (mNextAppTransitionScaleUp) {
- if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
- // Fade out while bringing up selected activity. This keeps the
- // current activity from showing through a launching wallpaper
- // activity.
- a = new AlphaAnimation(1, 0);
- } else {
- // noop animation
- a = new AlphaAnimation(1, 1);
- }
- } else {
- float scaleW = thumbWidth / appWidth;
- float scaleH = thumbHeight / appHeight;
- Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mNextAppTransitionStartX, scaleW),
- computePivot(mNextAppTransitionStartY, scaleH));
-
- Animation alpha = new AlphaAnimation(1, 0);
-
- AnimationSet set = new AnimationSet(true);
- set.addAnimation(scale);
- set.addAnimation(alpha);
- set.setZAdjustment(Animation.ZORDER_TOP);
- a = set;
- }
- }
-
+ /**
+ * Prepares the specified animation with a standard duration, interpolator, etc.
+ */
+ Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
// Pick the desired duration. If this is an inter-activity transition,
// it is the standard duration for that. Otherwise we use the longer
// task transition duration.
@@ -478,9 +421,223 @@
return a;
}
+ /**
+ * Return the current thumbnail transition state.
+ */
+ int getThumbnailTransitionState(boolean enter) {
+ if (enter) {
+ if (mNextAppTransitionScaleUp) {
+ return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+ } else {
+ return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
+ }
+ } else {
+ if (mNextAppTransitionScaleUp) {
+ return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+ } else {
+ return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
+ }
+ }
+ }
+
+ /**
+ * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
+ * when a thumbnail is specified with the activity options.
+ */
+ Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) {
+ Animation a;
+ final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+ final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+ final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+ final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+ if (mNextAppTransitionScaleUp) {
+ // Animation for the thumbnail zooming from its initial size to the full screen
+ float scaleW = appWidth / thumbWidth;
+ float scaleH = appHeight / thumbHeight;
+ Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
+ computePivot(mNextAppTransitionStartX, 1 / scaleW),
+ computePivot(mNextAppTransitionStartY, 1 / scaleH));
+ scale.setInterpolator(mDecelerateInterpolator);
+
+ Animation alpha = new AlphaAnimation(1, 0);
+ alpha.setInterpolator(mThumbnailFadeoutInterpolator);
+
+ // This AnimationSet uses the Interpolators assigned above.
+ AnimationSet set = new AnimationSet(false);
+ set.addAnimation(scale);
+ set.addAnimation(alpha);
+ a = set;
+ } else {
+ // Animation for the thumbnail zooming down from the full screen to its final size
+ float scaleW = appWidth / thumbWidth;
+ float scaleH = appHeight / thumbHeight;
+ a = new ScaleAnimation(scaleW, 1, scaleH, 1,
+ computePivot(mNextAppTransitionStartX, 1 / scaleW),
+ computePivot(mNextAppTransitionStartY, 1 / scaleH));
+ }
+
+ return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+ }
+
+ /**
+ * This alternate animation is created when we are doing a thumbnail transition, for the
+ * activity that is leaving, and the activity that is entering.
+ */
+ Animation createAlternateThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
+ int appHeight, int transit,
+ Rect containingFrame, Rect contentInsets) {
+ Animation a;
+ final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+ final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+ final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+ final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+ switch (thumbTransitState) {
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
+ // Entering app scales up with the thumbnail
+ float scale = thumbWidth / appWidth;
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
+ int scaledTopDecor = (int) (scale * contentInsets.top);
+ Rect fromClipRect = new Rect(containingFrame);
+ fromClipRect.bottom = (fromClipRect.top + unscaledThumbHeight);
+ Rect toClipRect = new Rect(containingFrame);
+
+ Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
+ computePivot(mNextAppTransitionStartX, scale),
+ computePivot(mNextAppTransitionStartY, scale));
+ Animation alphaAnim = new AlphaAnimation(1, 1);
+ Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect);
+ Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
+
+ AnimationSet set = new AnimationSet(true);
+ set.addAnimation(alphaAnim);
+ set.addAnimation(clipAnim);
+ set.addAnimation(scaleAnim);
+ set.addAnimation(translateAnim);
+ a = set;
+ break;
+ }
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
+ // Exiting app while the thumbnail is scaling up should fade
+ if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
+ // Fade out while bringing up selected activity. This keeps the
+ // current activity from showing through a launching wallpaper
+ // activity.
+ a = new AlphaAnimation(1, 0);
+ } else {
+ // noop animation
+ a = new AlphaAnimation(1, 1);
+ }
+ break;
+ }
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
+ // Entering the other app, it should just be visible while we scale the thumbnail
+ // down above it
+ a = new AlphaAnimation(1, 1);
+ break;
+ }
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+ // Exiting the current app, the app should scale down with the thumbnail
+ float scale = thumbWidth / appWidth;
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
+ int scaledTopDecor = (int) (scale * contentInsets.top);
+ Rect fromClipRect = new Rect(containingFrame);
+ Rect toClipRect = new Rect(containingFrame);
+ toClipRect.bottom = (toClipRect.top + unscaledThumbHeight);
+
+ Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
+ computePivot(mNextAppTransitionStartX, scale),
+ computePivot(mNextAppTransitionStartY, scale));
+ Animation alphaAnim = new AlphaAnimation(1, 1);
+ Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect);
+ Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
+
+ AnimationSet set = new AnimationSet(true);
+ set.addAnimation(alphaAnim);
+ set.addAnimation(clipAnim);
+ set.addAnimation(scaleAnim);
+ set.addAnimation(translateAnim);
+
+ a = set;
+ a.setZAdjustment(Animation.ZORDER_TOP);
+ break;
+ }
+ default:
+ throw new RuntimeException("Invalid thumbnail transition state");
+ }
+
+ return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+ }
+
+ /**
+ * This animation is created when we are doing a thumbnail transition, for the activity that is
+ * leaving, and the activity that is entering.
+ */
+ Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
+ int appHeight, int transit) {
+ Animation a;
+ final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+ final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+ final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+ final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+ switch (thumbTransitState) {
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
+ // Entering app scales up with the thumbnail
+ float scaleW = thumbWidth / appWidth;
+ float scaleH = thumbHeight / appHeight;
+ a = new ScaleAnimation(scaleW, 1, scaleH, 1,
+ computePivot(mNextAppTransitionStartX, scaleW),
+ computePivot(mNextAppTransitionStartY, scaleH));
+ break;
+ }
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
+ // Exiting app while the thumbnail is scaling up should fade or stay in place
+ if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
+ // Fade out while bringing up selected activity. This keeps the
+ // current activity from showing through a launching wallpaper
+ // activity.
+ a = new AlphaAnimation(1, 0);
+ } else {
+ // noop animation
+ a = new AlphaAnimation(1, 1);
+ }
+ break;
+ }
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
+ // Entering the other app, it should just be visible while we scale the thumbnail
+ // down above it
+ a = new AlphaAnimation(1, 1);
+ break;
+ }
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+ // Exiting the current app, the app should scale down with the thumbnail
+ float scaleW = thumbWidth / appWidth;
+ float scaleH = thumbHeight / appHeight;
+ Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
+ computePivot(mNextAppTransitionStartX, scaleW),
+ computePivot(mNextAppTransitionStartY, scaleH));
+
+ Animation alpha = new AlphaAnimation(1, 0);
+
+ AnimationSet set = new AnimationSet(true);
+ set.addAnimation(scale);
+ set.addAnimation(alpha);
+ set.setZAdjustment(Animation.ZORDER_TOP);
+ a = set;
+ break;
+ }
+ default:
+ throw new RuntimeException("Invalid thumbnail transition state");
+ }
+
+ return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+ }
+
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- int appWidth, int appHeight) {
+ int appWidth, int appHeight, Rect containingFrame, Rect contentInsets) {
Animation a;
if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
a = loadAnimation(mNextAppTransitionPackage, enter ?
@@ -501,7 +658,14 @@
mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
- a = createThumbnailAnimationLocked(transit, enter, false, appWidth, appHeight);
+ if (mUseAlternateThumbnailAnimation) {
+ a = createAlternateThumbnailEnterExitAnimationLocked(
+ getThumbnailTransitionState(enter), appWidth, appHeight, transit,
+ containingFrame, contentInsets);
+ } else {
+ a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
+ appWidth, appHeight, transit);
+ }
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
"ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index adfb2bd..4f80f1f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3155,7 +3155,22 @@
final int height = displayInfo.appHeight;
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation: atoken="
+ atoken);
- Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height);
+
+ // Determine the visible rect to calculate the thumbnail clip
+ WindowState win = atoken.findMainWindow();
+ Rect containingFrame = new Rect(0, 0, width, height);
+ Rect contentInsets = new Rect();
+ if (win != null) {
+ if (win.mContainingFrame != null) {
+ containingFrame.set(win.mContainingFrame);
+ }
+ if (win.mContentInsets != null) {
+ contentInsets.set(win.mContentInsets);
+ }
+ }
+
+ Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
+ containingFrame, contentInsets);
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = null;
@@ -8608,11 +8623,13 @@
wtoken.deferClearAllDrawn = false;
}
+ boolean useAlternateThumbnailAnimation =
+ SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false);
AppWindowAnimator appAnimator =
topOpeningApp == null ? null : topOpeningApp.mAppAnimator;
Bitmap nextAppTransitionThumbnail = mAppTransition.getNextAppTransitionThumbnail();
- if (nextAppTransitionThumbnail != null && appAnimator != null
- && appAnimator.animation != null) {
+ if (!useAlternateThumbnailAnimation && nextAppTransitionThumbnail != null
+ && appAnimator != null && appAnimator.animation != null) {
// This thumbnail animation is very special, we need to have
// an extra surface with the thumbnail included with the animation.
Rect dirty = new Rect(0, 0, nextAppTransitionThumbnail.getWidth(),
@@ -8636,8 +8653,8 @@
drawSurface.release();
appAnimator.thumbnailLayer = topOpeningLayer;
DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
- Animation anim = mAppTransition.createThumbnailAnimationLocked(
- transit, true, true, displayInfo.appWidth, displayInfo.appHeight);
+ Animation anim = mAppTransition.createThumbnailScaleAnimationLocked(
+ displayInfo.appWidth, displayInfo.appHeight, transit);
appAnimator.thumbnailAnimation = anim;
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mTransitionAnimationScale);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2c0e99e..3434053 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -709,6 +709,11 @@
return mAppToken != null ? mAppToken.appToken : null;
}
+ @Override
+ public int getTaskId() {
+ return mAppToken != null ? mAppToken.groupId : UNKNOWN_TASK_ID;
+ }
+
boolean setInsetsChanged() {
mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 2cd6000..6b3c368 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -113,6 +113,11 @@
float mAlpha = 0;
float mLastAlpha = 0;
+ boolean mHasClipRect;
+ Rect mClipRect = new Rect();
+ Rect mTmpClipRect = new Rect();
+ Rect mLastClipRect = new Rect();
+
// Used to save animation distances between the time they are calculated and when they are
// used.
int mAnimDw;
@@ -951,6 +956,7 @@
if (screenAnimation) {
tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
}
+
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
MagnificationSpec spec = mService.mAccessibilityController
@@ -985,6 +991,7 @@
// transforming since it is more important to have that
// animation be smooth.
mShownAlpha = mAlpha;
+ mHasClipRect = false;
if (!mService.mLimitedAlphaCompositing
|| (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
|| (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
@@ -998,6 +1005,10 @@
}
if (appTransformation != null) {
mShownAlpha *= appTransformation.getAlpha();
+ if (appTransformation.hasClipRect()) {
+ mClipRect.set(appTransformation.getClipRect());
+ mHasClipRect = true;
+ }
}
if (mAnimator.mUniverseBackground != null) {
mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
@@ -1149,15 +1160,32 @@
applyDecorRect(w.mDecorFrame);
}
- if (!w.mSystemDecorRect.equals(w.mLastSystemDecorRect)) {
- w.mLastSystemDecorRect.set(w.mSystemDecorRect);
+ // By default, the clip rect is the system decor rect
+ Rect clipRect = w.mSystemDecorRect;
+ if (mHasClipRect) {
+
+ // If we have an animated clip rect, intersect it with the system decor rect
+ // NOTE: We are adding a temporary workaround due to the status bar not always reporting
+ // the correct system decor rect. In such cases, we take into account the specified
+ // content insets as well.
+ int offsetTop = Math.max(w.mSystemDecorRect.top, w.mContentInsets.top);
+ mTmpClipRect.set(w.mSystemDecorRect);
+ mTmpClipRect.offset(0, -offsetTop);
+ mTmpClipRect.intersect(mClipRect);
+ mTmpClipRect.offset(0, offsetTop);
+ clipRect = mTmpClipRect;
+
+ }
+
+ if (!clipRect.equals(mLastClipRect)) {
+ mLastClipRect.set(clipRect);
try {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
- "CROP " + w.mSystemDecorRect.toShortString(), null);
- mSurfaceControl.setWindowCrop(w.mSystemDecorRect);
+ "CROP " + clipRect.toShortString(), null);
+ mSurfaceControl.setWindowCrop(clipRect);
} catch (RuntimeException e) {
Slog.w(TAG, "Error setting crop surface of " + w
- + " crop=" + w.mSystemDecorRect.toShortString(), e);
+ + " crop=" + clipRect.toShortString(), e);
if (!recoveringMemory) {
mService.reclaimSomeSurfaceMemoryLocked(this, "crop", true);
}
@@ -1302,8 +1330,8 @@
mSurfaceLayer = mAnimLayer;
mSurfaceControl.setLayer(mAnimLayer);
mSurfaceControl.setMatrix(
- mDsDx*w.mHScale, mDtDx*w.mVScale,
- mDsDy*w.mHScale, mDtDy*w.mVScale);
+ mDsDx * w.mHScale, mDtDx * w.mVScale,
+ mDsDy * w.mHScale, mDtDy * w.mVScale);
if (mLastHidden && mDrawState == HAS_DRAWN) {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 0607925..1b3887c 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -8,23 +8,26 @@
$(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_am_BatteryStatsService.cpp \
$(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
$(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
$(LOCAL_REL_DIR)/com_android_server_dreams_McuHal.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecService.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
$(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_location_GpsLocationProvider.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \
$(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
$(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
- $(LOCAL_REL_DIR)/com_android_server_location_GpsLocationProvider.cpp \
- $(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \
- $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
$(LOCAL_REL_DIR)/onload.cpp
+include external/stlport/libstlport.mk
+
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
frameworks/base/services \
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
new file mode 100644
index 0000000..6170c09
--- /dev/null
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
@@ -0,0 +1,689 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "HdmiCecJni"
+
+#define LOG_NDEBUG 1
+
+#include "ScopedPrimitiveArray.h"
+
+#include <cstring>
+#include <deque>
+#include <map>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <hardware/hdmi_cec.h>
+
+namespace android {
+
+static struct {
+ jmethodID handleMessage;
+ jmethodID handleHotplug;
+ jmethodID getActiveSource;
+ jmethodID getOsdName;
+ jmethodID getLanguage;
+} gHdmiCecServiceClassInfo;
+
+#ifndef min
+#define min(a, b) ((a) > (b) ? (b) : (a))
+#endif
+
+class HdmiCecHandler {
+public:
+ enum HdmiCecError {
+ SUCCESS = 0,
+ FAILED = -1
+ };
+
+ // Data type to hold a CEC message or internal event data.
+ typedef union {
+ cec_message_t cec;
+ hotplug_event_t hotplug;
+ } queue_item_t;
+
+ // Entry used for message queue.
+ typedef std::pair<int, const queue_item_t> MessageEntry;
+
+ HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj);
+
+ void initialize();
+
+ // initialize individual logical device.
+ int initLogicalDevice(int type);
+ void releaseLogicalDevice(int type);
+
+ cec_logical_address_t getLogicalAddress(int deviceType);
+ int getDeviceType(cec_logical_address_t addr);
+ void queueMessage(const MessageEntry& message);
+ void queueOutgoingMessage(const cec_message_t& message);
+ void sendReportPhysicalAddress();
+ void sendActiveSource(cec_logical_address_t srcAddr);
+ void sendInactiveSource(cec_logical_address_t srcAddr);
+ void sendImageViewOn(cec_logical_address_t srcAddr);
+ void sendTextViewOn(cec_logical_address_t srcAddr);
+ void sendGiveDevicePowerStatus(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+ void sendFeatureAbort(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+ int opcode, int reason);
+ void sendCecVersion(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+ int version);
+ void sendDeviceVendorID(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+ void sendGiveDeviceVendorID(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+ void sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+ const char* name, size_t len);
+ void sendSetMenuLanguage(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+
+ void sendCecMessage(const cec_message_t& message);
+
+private:
+ enum {
+ EVENT_TYPE_RX,
+ EVENT_TYPE_TX,
+ EVENT_TYPE_HOTPLUG,
+ EVENT_TYPE_STANDBY
+ };
+
+ static const unsigned int MAX_BUFFER_SIZE = 256;
+ static const uint16_t INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+ static const int INACTIVE_DEVICE_TYPE = -1;
+
+ static void onReceived(const hdmi_event_t* event, void* arg);
+ static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
+
+ void updatePhysicalAddress();
+ void dispatchMessage(const MessageEntry& message);
+ void processIncomingMessage(const cec_message_t& msg);
+
+ // Check the message before we pass it up to framework. If true, we proceed.
+ // otherwise do not propagate it.
+ bool precheckMessage(const cec_message_t& msg);
+
+ // Propagate the message up to Java layer.
+ void propagateMessage(const cec_message_t& msg);
+ void propagateHotplug(bool connected);
+
+ // Handles incoming <Request Active Source> message. If one of logical
+ // devices is active, it should reply with <Active Source> message.
+ void handleRequestActiveSource();
+ void handleGetOsdName(const cec_message_t& msg);
+ void handleGiveDeviceVendorID(const cec_message_t& msg);
+ void handleGetCECVersion(const cec_message_t& msg);
+ void handleGetMenuLanguage(const cec_message_t& msg);
+
+ // Internal thread for message queue handler
+ class HdmiThread : public Thread {
+ public:
+ HdmiThread(HdmiCecHandler* hdmiCecHandler, bool canCallJava) :
+ Thread(canCallJava),
+ mHdmiCecHandler(hdmiCecHandler) {
+ }
+ private:
+ virtual bool threadLoop() {
+ ALOGV("HdmiThread started");
+ AutoMutex _l(mHdmiCecHandler->mMessageQueueLock);
+ mHdmiCecHandler->mMessageQueueCondition.wait(mHdmiCecHandler->mMessageQueueLock);
+ /* Process all messages in the queue */
+ while (mHdmiCecHandler->mMessageQueue.size() > 0) {
+ MessageEntry entry = mHdmiCecHandler->mMessageQueue.front();
+ mHdmiCecHandler->dispatchMessage(entry);
+ }
+ return true;
+ }
+
+ HdmiCecHandler* mHdmiCecHandler;
+ };
+
+ // device type -> logical address mapping
+ std::map<int, cec_logical_address_t> mLogicalDevices;
+
+ hdmi_cec_device_t* mDevice;
+ jobject mCallbacksObj;
+ Mutex mLock;
+ Mutex mMessageQueueLock;
+ Condition mMessageQueueCondition;
+ sp<HdmiThread> mMessageQueueHandler;
+
+ std::deque<MessageEntry> mMessageQueue;
+ uint16_t mPhysicalAddress;
+};
+
+
+HdmiCecHandler::HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj) :
+ mDevice(device),
+ mCallbacksObj(callbacksObj) {
+}
+
+void HdmiCecHandler::initialize() {
+ mDevice->register_event_callback(mDevice, HdmiCecHandler::onReceived, this);
+ mMessageQueueHandler = new HdmiThread(this, true /* canCallJava */);
+ mMessageQueueHandler->run("MessageHandler");
+ updatePhysicalAddress();
+}
+
+void HdmiCecHandler::updatePhysicalAddress() {
+ uint16_t addr;
+ if (!mDevice->get_physical_address(mDevice, &addr)) {
+ mPhysicalAddress = addr;
+ } else {
+ mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+ }
+}
+
+int HdmiCecHandler::initLogicalDevice(int type) {
+ cec_logical_address_t addr;
+ int res = mDevice->allocate_logical_address(mDevice, type, &addr);
+
+ if (res != 0) {
+ ALOGE("Logical Address Allocation failed: %d", res);
+ } else {
+ ALOGV("Logical Address Allocation success: %d", addr);
+ mLogicalDevices.insert(std::pair<int, cec_logical_address_t>(type, addr));
+ }
+ return addr;
+}
+
+void HdmiCecHandler::releaseLogicalDevice(int type) {
+ std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.find(type);
+ if (it != mLogicalDevices.end()) {
+ mLogicalDevices.erase(it);
+ }
+ // TODO: remove the address monitored in HAL as well.
+}
+
+cec_logical_address_t HdmiCecHandler::getLogicalAddress(int mDevicetype) {
+ std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.find(mDevicetype);
+ if (it != mLogicalDevices.end()) {
+ return it->second;
+ }
+ return CEC_ADDR_UNREGISTERED;
+}
+
+int HdmiCecHandler::getDeviceType(cec_logical_address_t addr) {
+ std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
+ for (; it != mLogicalDevices.end(); ++it) {
+ if (it->second == addr) {
+ return it->first;
+ }
+ }
+ return INACTIVE_DEVICE_TYPE;
+}
+
+void HdmiCecHandler::queueMessage(const MessageEntry& entry) {
+ AutoMutex _l(mMessageQueueLock);
+ if (mMessageQueue.size() <= MAX_BUFFER_SIZE) {
+ mMessageQueue.push_back(entry);
+ mMessageQueueCondition.signal();
+ } else {
+ ALOGW("Queue is full! Message dropped.");
+ }
+}
+
+void HdmiCecHandler::queueOutgoingMessage(const cec_message_t& message) {
+ queue_item_t item;
+ item.cec = message;
+ MessageEntry entry = std::make_pair(EVENT_TYPE_TX, item);
+ queueMessage(entry);
+}
+
+void HdmiCecHandler::sendReportPhysicalAddress() {
+ if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+ ALOGE("Invalid physical address.");
+ return;
+ }
+
+ // Report physical address for each logical one hosted in it.
+ std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
+ while (it != mLogicalDevices.end()) {
+ cec_message_t msg;
+ msg.initiator = it->second; // logical address
+ msg.destination = CEC_ADDR_BROADCAST;
+ msg.length = 4;
+ msg.body[0] = CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS;
+ std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
+ msg.body[3] = it->first; // device type
+ queueOutgoingMessage(msg);
+ ++it;
+ }
+}
+
+void HdmiCecHandler::sendActiveSource(cec_logical_address_t srcAddr) {
+ if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+ ALOGE("Error getting physical address.");
+ return;
+ }
+ cec_message_t msg;
+ msg.initiator = srcAddr;
+ msg.destination = CEC_ADDR_BROADCAST;
+ msg.length = 3;
+ msg.body[0] = CEC_MESSAGE_ACTIVE_SOURCE;
+ std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
+ queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendInactiveSource(cec_logical_address_t srcAddr) {
+ cec_message_t msg;
+ msg.initiator = srcAddr;
+ msg.destination = CEC_ADDR_TV;
+ msg.length = 3;
+ msg.body[0] = CEC_MESSAGE_INACTIVE_SOURCE;
+ if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) {
+ std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
+ queueOutgoingMessage(msg);
+ }
+}
+
+void HdmiCecHandler::sendImageViewOn(cec_logical_address_t srcAddr) {
+ cec_message_t msg;
+ msg.initiator = srcAddr;
+ msg.destination = CEC_ADDR_TV;
+ msg.length = 1;
+ msg.body[0] = CEC_MESSAGE_IMAGE_VIEW_ON;
+ queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendTextViewOn(cec_logical_address_t srcAddr) {
+ cec_message_t msg;
+ msg.initiator = srcAddr;
+ msg.destination = CEC_ADDR_TV;
+ msg.length = 1;
+ msg.body[0] = CEC_MESSAGE_TEXT_VIEW_ON;
+ queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendGiveDevicePowerStatus(cec_logical_address_t srcAddr,
+ cec_logical_address_t dstAddr) {
+ cec_message_t msg;
+ msg.initiator = srcAddr;
+ msg.destination = dstAddr;
+ msg.length = 1;
+ msg.body[0] = CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS;
+ queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendFeatureAbort(cec_logical_address_t srcAddr,
+ cec_logical_address_t dstAddr, int opcode, int reason) {
+ cec_message_t msg;
+ msg.initiator = srcAddr;
+ msg.destination = dstAddr;
+ msg.length = 3;
+ msg.body[0] = CEC_MESSAGE_FEATURE_ABORT;
+ msg.body[1] = opcode;
+ msg.body[2] = reason;
+ queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendCecVersion(cec_logical_address_t srcAddr,
+ cec_logical_address_t dstAddr, int version) {
+ cec_message_t msg;
+ msg.initiator = srcAddr;
+ msg.destination = dstAddr;
+ msg.length = 2;
+ msg.body[0] = CEC_MESSAGE_CEC_VERSION;
+ msg.body[1] = version;
+ queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendGiveDeviceVendorID(cec_logical_address_t srcAddr,
+ cec_logical_address_t dstAddr) {
+ cec_message_t msg;
+ msg.initiator = srcAddr;
+ msg.destination = dstAddr;
+ msg.length = 1;
+ msg.body[0] = CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID;
+ queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendDeviceVendorID(cec_logical_address_t srcAddr,
+ cec_logical_address_t dstAddr) {
+ cec_message_t msg;
+ msg.initiator = srcAddr;
+ msg.destination = dstAddr;
+ msg.length = 4;
+ msg.body[0] = CEC_MESSAGE_DEVICE_VENDOR_ID;
+ uint32_t vendor_id;
+ mDevice->get_vendor_id(mDevice, &vendor_id);
+ std::memcpy(msg.body + 1, &vendor_id, 3);
+ queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+ const char* name, size_t len) {
+ cec_message_t msg;
+ msg.initiator = srcAddr;
+ msg.destination = dstAddr;
+ msg.body[0] = CEC_MESSAGE_SET_OSD_NAME;
+ msg.length = min(len + 1, CEC_MESSAGE_BODY_MAX_LENGTH);
+ std::memcpy(msg.body + 1, name, msg.length - 1);
+ queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendSetMenuLanguage(cec_logical_address_t srcAddr,
+ cec_logical_address_t dstAddr) {
+ char lang[4]; // buffer for 3-letter language code
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jstring res = (jstring) env->CallObjectMethod(mCallbacksObj,
+ gHdmiCecServiceClassInfo.getLanguage,
+ getDeviceType(srcAddr));
+ const char *clang = env->GetStringUTFChars(res, NULL);
+ strlcpy(lang, clang, sizeof(lang));
+ env->ReleaseStringUTFChars(res, clang);
+
+ cec_message_t msg;
+ msg.initiator = srcAddr;
+ msg.destination = dstAddr;
+ msg.length = 4; // opcode (1) + language code (3)
+ msg.body[0] = CEC_MESSAGE_SET_MENU_LANGUAGE;
+ std::memcpy(msg.body + 1, lang, 3);
+ queueOutgoingMessage(msg);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::sendCecMessage(const cec_message_t& message) {
+ AutoMutex _l(mLock);
+ ALOGV("sendCecMessage");
+ mDevice->send_message(mDevice, &message);
+}
+
+// static
+void HdmiCecHandler::onReceived(const hdmi_event_t* event, void* arg) {
+ HdmiCecHandler* handler = static_cast<HdmiCecHandler*>(arg);
+ if (handler == NULL) {
+ return;
+ }
+ queue_item_t item;
+ if (event->type == HDMI_EVENT_CEC_MESSAGE) {
+ item.cec = event->cec;
+ MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_RX, item);
+ handler->queueMessage(entry);
+ } else if (event->type == HDMI_EVENT_HOT_PLUG) {
+ item.hotplug = event->hotplug;
+ MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_HOTPLUG, item);
+ handler->queueMessage(entry);
+ }
+}
+
+// static
+void HdmiCecHandler::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ ALOGE("An exception was thrown by callback '%s'.", methodName);
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+}
+
+void HdmiCecHandler::dispatchMessage(const MessageEntry& entry) {
+ int type = entry.first;
+ mMessageQueueLock.unlock();
+ if (type == EVENT_TYPE_RX) {
+ mMessageQueue.pop_front();
+ processIncomingMessage(entry.second.cec);
+ } else if (type == EVENT_TYPE_TX) {
+ sendCecMessage(entry.second.cec);
+ mMessageQueue.pop_front();
+ } else if (type == EVENT_TYPE_HOTPLUG) {
+ mMessageQueue.pop_front();
+ bool connected = entry.second.hotplug.connected;
+ if (connected) {
+ // TODO: Update logical addresses as well, since they also could have
+ // changed while the cable was disconnected.
+ updatePhysicalAddress();
+ }
+ propagateHotplug(connected);
+ }
+ mMessageQueueLock.lock();
+}
+
+void HdmiCecHandler::processIncomingMessage(const cec_message_t& msg) {
+ int opcode = msg.body[0];
+ if (opcode == CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS) {
+ sendReportPhysicalAddress();
+ } else if (opcode == CEC_MESSAGE_REQUEST_ACTIVE_SOURCE) {
+ handleRequestActiveSource();
+ } else if (opcode == CEC_MESSAGE_GET_OSD_NAME) {
+ handleGetOsdName(msg);
+ } else if (opcode == CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID) {
+ handleGiveDeviceVendorID(msg);
+ } else if (opcode == CEC_MESSAGE_GET_CEC_VERSION) {
+ handleGetCECVersion(msg);
+ } else if (opcode == CEC_MESSAGE_GET_MENU_LANGUAGE) {
+ handleGetMenuLanguage(msg);
+ } else {
+ if (precheckMessage(msg)) {
+ propagateMessage(msg);
+ }
+ }
+}
+
+bool HdmiCecHandler::precheckMessage(const cec_message_t& msg) {
+ // Check if this is the broadcast message coming to itself, which need not be passed
+ // back to framework. This happens because CEC spec specifies that a physical device
+ // may host multiple logical devices. A broadcast message sent by one of them therefore
+ // should be able to reach the others by the loopback mechanism.
+ //
+ // Currently we don't deal with multiple logical devices, so this is not necessary.
+ // It should be revisited once we support hosting multiple logical devices.
+ int opcode = msg.body[0];
+ if (msg.destination == CEC_ADDR_BROADCAST &&
+ (opcode == CEC_MESSAGE_ACTIVE_SOURCE ||
+ opcode == CEC_MESSAGE_SET_STREAM_PATH ||
+ opcode == CEC_MESSAGE_INACTIVE_SOURCE)) {
+ uint16_t senderAddr;
+ std::memcpy(&senderAddr, &msg.body[1], 2);
+ if (senderAddr == mPhysicalAddress) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void HdmiCecHandler::propagateMessage(const cec_message_t& msg) {
+ int paramLen = msg.length - 1;
+ jint srcAddr = msg.initiator;
+ jint dstAddr = msg.destination;
+ jint opcode = msg.body[0];
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jbyteArray params = env->NewByteArray(paramLen);
+ const jbyte* body = reinterpret_cast<const jbyte *>(msg.body + 1);
+ if (paramLen > 0) {
+ env->SetByteArrayRegion(params, 0, paramLen, body);
+ }
+ env->CallVoidMethod(mCallbacksObj,
+ gHdmiCecServiceClassInfo.handleMessage,
+ srcAddr, dstAddr, opcode, params);
+ env->DeleteLocalRef(params);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::propagateHotplug(bool connected) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mCallbacksObj,
+ gHdmiCecServiceClassInfo.handleHotplug,
+ connected);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+
+void HdmiCecHandler::handleRequestActiveSource() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jint activeDeviceType = env->CallIntMethod(mCallbacksObj,
+ gHdmiCecServiceClassInfo.getActiveSource);
+ if (activeDeviceType != INACTIVE_DEVICE_TYPE) {
+ sendActiveSource(getLogicalAddress(activeDeviceType));
+ }
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::handleGetOsdName(const cec_message_t& msg) {
+ cec_logical_address_t addr = msg.destination;
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jbyteArray res = (jbyteArray) env->CallObjectMethod(mCallbacksObj,
+ gHdmiCecServiceClassInfo.getOsdName,
+ getDeviceType(addr));
+ jbyte *name = env->GetByteArrayElements(res, NULL);
+ if (name != NULL) {
+ sendSetOsdName(addr, msg.initiator, reinterpret_cast<const char *>(name),
+ env->GetArrayLength(res));
+ env->ReleaseByteArrayElements(res, name, JNI_ABORT);
+ }
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::handleGiveDeviceVendorID(const cec_message_t& msg) {
+ sendDeviceVendorID(msg.destination, msg.initiator);
+}
+
+void HdmiCecHandler::handleGetCECVersion(const cec_message_t& msg) {
+ int version;
+ mDevice->get_version(mDevice, &version);
+ sendCecVersion(msg.destination, msg.initiator, version);
+}
+
+void HdmiCecHandler::handleGetMenuLanguage(const cec_message_t& msg) {
+ sendSetMenuLanguage(msg.destination, msg.initiator);
+}
+
+//------------------------------------------------------------------------------
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) {
+ int err;
+ hw_module_t* module;
+ err = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID, const_cast<const hw_module_t **>(&module));
+ if (err != 0) {
+ ALOGE("Error acquiring hardware module: %d", err);
+ return 0;
+ }
+ hw_device_t* device;
+ err = module->methods->open(module, HDMI_CEC_HARDWARE_INTERFACE, &device);
+ if (err != 0) {
+ ALOGE("Error opening hardware module: %d", err);
+ return 0;
+ }
+ HdmiCecHandler *handler = new HdmiCecHandler(reinterpret_cast<hdmi_cec_device *>(device),
+ env->NewGlobalRef(callbacksObj));
+ handler->initialize();
+
+ GET_METHOD_ID(gHdmiCecServiceClassInfo.handleMessage, clazz,
+ "handleMessage", "(III[B)V");
+ GET_METHOD_ID(gHdmiCecServiceClassInfo.handleHotplug, clazz,
+ "handleHotplug", "(Z)V");
+ GET_METHOD_ID(gHdmiCecServiceClassInfo.getActiveSource, clazz,
+ "getActiveSource", "()I");
+ GET_METHOD_ID(gHdmiCecServiceClassInfo.getOsdName, clazz,
+ "getOsdName", "(I)[B");
+ GET_METHOD_ID(gHdmiCecServiceClassInfo.getLanguage, clazz,
+ "getLanguage", "(I)Ljava/lang/String;");
+
+ return reinterpret_cast<jlong>(handler);
+}
+
+static void nativeSendMessage(JNIEnv* env, jclass clazz, jlong handlerPtr, jint deviceType,
+ jint dstAddr, jint opcode, jbyteArray params) {
+ HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+ cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+ jsize len = env->GetArrayLength(params);
+ ScopedByteArrayRO paramsPtr(env, params);
+ cec_message_t message;
+ message.initiator = srcAddr;
+ message.destination = static_cast<cec_logical_address_t>(dstAddr);
+ message.length = len + 1;
+ message.body[0] = opcode;
+ std::memcpy(message.body + 1, paramsPtr.get(), len);
+ handler->sendCecMessage(message);
+}
+
+static int nativeAllocateLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr,
+ jint deviceType) {
+ HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+ return handler->initLogicalDevice(deviceType);
+}
+
+static void nativeRemoveLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr,
+ jint deviceType) {
+ HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+ return handler->releaseLogicalDevice(deviceType);
+}
+
+static void nativeSendActiveSource(JNIEnv* env, jclass clazz, jlong handlerPtr,
+ jint deviceType) {
+ HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+ cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+ handler->sendActiveSource(srcAddr);
+}
+
+static void nativeSendInactiveSource(JNIEnv* env, jclass clazz, jlong handlerPtr,
+ jint deviceType) {
+ HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+ cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+ handler->sendInactiveSource(srcAddr);
+}
+
+static void nativeSendImageViewOn(JNIEnv* env, jclass clazz, jlong handlerPtr,
+ jint deviceType) {
+ HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+ cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+ handler->sendImageViewOn(srcAddr);
+}
+
+static void nativeSendTextViewOn(JNIEnv* env, jclass clazz, jlong handlerPtr,
+ jint deviceType) {
+ HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+ cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+ handler->sendTextViewOn(srcAddr);
+}
+
+static void nativeSendGiveDevicePowerStatus(JNIEnv* env, jclass clazz, jlong handlerPtr,
+ jint deviceType, jint destination) {
+ HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+ cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+ cec_logical_address_t dstAddr = static_cast<cec_logical_address_t>(destination);
+ handler->sendGiveDevicePowerStatus(srcAddr, dstAddr);
+}
+
+static JNINativeMethod sMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecService;)J",
+ (void *)nativeInit },
+ { "nativeSendMessage", "(JIII[B)V",
+ (void *)nativeSendMessage },
+ { "nativeAllocateLogicalAddress", "(JI)I",
+ (void *)nativeAllocateLogicalAddress },
+ { "nativeRemoveLogicalAddress", "(JI)V",
+ (void *)nativeRemoveLogicalAddress },
+ { "nativeSendActiveSource", "(JI)V",
+ (void *)nativeSendActiveSource },
+ { "nativeSendInactiveSource", "(JI)V",
+ (void *)nativeSendInactiveSource },
+ { "nativeSendImageViewOn", "(JI)V",
+ (void *)nativeSendImageViewOn },
+ { "nativeSendTextViewOn", "(JI)V",
+ (void *)nativeSendTextViewOn },
+ { "nativeSendGiveDevicePowerStatus", "(JII)V",
+ (void *)nativeSendGiveDevicePowerStatus }
+};
+
+#define CLASS_PATH "com/android/server/hdmi/HdmiCecService"
+
+int register_android_server_hdmi_HdmiCecService(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+ return 0;
+}
+
+} /* namespace android */
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index ac6585b..bf9f7f4 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -21,6 +21,7 @@
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
+int register_android_server_AssetAtlasService(JNIEnv* env);
int register_android_server_BatteryStatsService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
@@ -29,15 +30,15 @@
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
+int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
-int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
-int register_android_server_AssetAtlasService(JNIEnv* env);
int register_android_server_dreams_McuHal(JNIEnv* env);
+int register_android_server_hdmi_HdmiCecService(JNIEnv* env);
};
using namespace android;
@@ -71,6 +72,7 @@
register_android_server_ConsumerIrService(env);
register_android_server_dreams_McuHal(env);
register_android_server_BatteryStatsService(env);
+ register_android_server_hdmi_HdmiCecService(env);
return JNI_VERSION_1_4;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b4c099d..c925669 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -118,6 +118,8 @@
"com.android.server.wifi.WifiService";
private static final String WIFI_P2P_SERVICE_CLASS =
"com.android.server.wifi.p2p.WifiP2pService";
+ private static final String HDMI_CEC_SERVICE_CLASS =
+ "com.android.server.hdmi.HdmiCecService";
private final int mFactoryTestMode;
private Timer mProfilerSnapshotTimer;
@@ -889,6 +891,12 @@
reportWtf("starting MediaSessionService", e);
}
+ try {
+ mSystemServiceManager.startService(HDMI_CEC_SERVICE_CLASS);
+ } catch (Throwable e) {
+ reportWtf("starting HdmiCec Service", e);
+ }
+
if (!disableNonCoreServices) {
try {
Slog.i(TAG, "Media Router Service");
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index 37176d6..b0296a0 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -18,12 +18,13 @@
import android.accounts.Account;
import android.content.ContentResolver;
+import android.content.Context;
import android.os.Bundle;
+import android.os.SystemClock;
+import android.provider.Settings;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.server.content.SyncOperation;
-
/**
* You can run those tests with:
*
@@ -35,6 +36,21 @@
public class SyncOperationTest extends AndroidTestCase {
+ Account mDummy;
+ /** Indicate an unimportant long that we're not testing. */
+ long mUnimportantLong = 0L;
+ /** Empty bundle. */
+ Bundle mEmpty;
+ /** Silly authority. */
+ String mAuthority;
+
+ @Override
+ public void setUp() {
+ mDummy = new Account("account1", "type1");
+ mEmpty = new Bundle();
+ mAuthority = "authority1";
+ }
+
@SmallTest
public void testToKey() {
Account account1 = new Account("account1", "type1");
@@ -111,35 +127,64 @@
@SmallTest
public void testCompareTo() {
- Account dummy = new Account("account1", "type1");
- Bundle b1 = new Bundle();
- final long unimportant = 0L;
long soon = 1000;
long soonFlex = 50;
long after = 1500;
long afterFlex = 100;
- SyncOperation op1 = new SyncOperation(dummy, 0, 0, SyncOperation.REASON_PERIODIC,
- "authority1", b1, soon, soonFlex, unimportant, unimportant, true);
+ SyncOperation op1 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+ "authority1", mEmpty, soon, soonFlex, mUnimportantLong, mUnimportantLong, true);
// Interval disjoint from and after op1.
- SyncOperation op2 = new SyncOperation(dummy, 0, 0, SyncOperation.REASON_PERIODIC,
- "authority1", b1, after, afterFlex, unimportant, unimportant, true);
+ SyncOperation op2 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+ "authority1", mEmpty, after, afterFlex, mUnimportantLong, mUnimportantLong, true);
// Interval equivalent to op1, but expedited.
Bundle b2 = new Bundle();
b2.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- SyncOperation op3 = new SyncOperation(dummy, 0, 0, 0,
- "authority1", b2, soon, soonFlex, unimportant, unimportant, true);
+ SyncOperation op3 = new SyncOperation(mDummy, 0, 0, 0,
+ "authority1", b2, -1, soonFlex, mUnimportantLong, mUnimportantLong, true);
// Interval overlaps but not equivalent to op1.
- SyncOperation op4 = new SyncOperation(dummy, 0, 0, SyncOperation.REASON_PERIODIC,
- "authority1", b1, soon + 100, soonFlex + 100, unimportant, unimportant, true);
+ SyncOperation op4 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+ "authority1", mEmpty, soon + 100, soonFlex + 100, mUnimportantLong, mUnimportantLong, true);
assertTrue(op1.compareTo(op2) == -1);
assertTrue("less than not transitive.", op2.compareTo(op1) == 1);
- assertTrue(op1.compareTo(op3) == 1);
+ assertTrue("Expedited sync not smaller than non-expedited.", op1.compareTo(op3) == 1);
assertTrue("greater than not transitive. ", op3.compareTo(op1) == -1);
- assertTrue("overlapping intervals not the same.", op1.compareTo(op4) == 0);
- assertTrue("equality not transitive.", op4.compareTo(op1) == 0);
+ assertTrue("overlapping intervals not correctly compared.", op1.compareTo(op4) == -1);
+ assertTrue("equality not transitive.", op4.compareTo(op1) == 1);
+ }
+
+ @SmallTest
+ public void testCopyConstructor() {
+ long fiveSecondsFromNow = 5 * 1000L;
+ long twoSecondsFlex = 2 * 1000L;
+ long eightSeconds = 8 * 1000L;
+ long fourSeconds = 4 * 1000L;
+
+ Bundle withExpedited = new Bundle();
+ withExpedited.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+ SyncOperation op = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_USER_START,
+ mAuthority, withExpedited, fiveSecondsFromNow, twoSecondsFlex,
+ eightSeconds /* backoff */, fourSeconds /* delayUntil */, true);
+ // Create another sync op to be rerun in 5 minutes.
+ long now = SystemClock.elapsedRealtime();
+ SyncOperation copy = new SyncOperation(op, fiveSecondsFromNow * 60);
+ // Copying an expedited sync to be re-run should not keep expedited property.
+ assertFalse("A rescheduled sync based off an expedited should not be expedited!",
+ copy.isExpedited());
+ assertFalse("A rescheduled sync based off an expedited should not have expedited=true in"
+ + "its bundle.",
+ copy.extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false));
+ assertTrue("Copied sync is not respecting new provided run-time.",
+ copy.latestRunTime == (now + fiveSecondsFromNow * 60));
+ assertTrue("A rescheduled sync should not have any flex.",
+ copy.flexTime == 0L);
+ assertTrue("A rescheduled op should honour the old op's backoff.",
+ copy.backoff == eightSeconds);
+ assertTrue("A rescheduled op should honour the old op's delayUntil param.",
+ copy.delayUntil == fourSeconds);
+
}
}
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
index 70fd810..ae1967e 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
@@ -131,7 +131,7 @@
assertEquals(sop.target.userId, popRetrieved.target.userId);
assertEquals(sop.reason, popRetrieved.reason);
assertEquals(sop.syncSource, popRetrieved.syncSource);
- assertEquals(sop.expedited, popRetrieved.expedited);
+ assertEquals(sop.isExpedited(), popRetrieved.expedited);
assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras));
}
@@ -169,7 +169,7 @@
assertEquals(deleted.target.userId, popDeleted.target.userId);
assertEquals(deleted.reason, popDeleted.reason);
assertEquals(deleted.syncSource, popDeleted.syncSource);
- assertEquals(deleted.expedited, popDeleted.expedited);
+ assertEquals(deleted.isExpedited(), popDeleted.expedited);
assert(android.content.PeriodicSync.syncExtrasEquals(deleted.extras, popDeleted.extras));
// Delete one to force write-all
engine.deleteFromPending(popDeleted);
@@ -192,7 +192,7 @@
assertEquals(sop.target.userId, popRetrieved.target.userId);
assertEquals(sop.reason, popRetrieved.reason);
assertEquals(sop.syncSource, popRetrieved.syncSource);
- assertEquals(sop.expedited, popRetrieved.expedited);
+ assertEquals(sop.isExpedited(), popRetrieved.expedited);
assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras));
popRetrieved = pops.get(1);
@@ -202,7 +202,7 @@
assertEquals(sop1.target.userId, popRetrieved.target.userId);
assertEquals(sop1.reason, popRetrieved.reason);
assertEquals(sop1.syncSource, popRetrieved.syncSource);
- assertEquals(sop1.expedited, popRetrieved.expedited);
+ assertEquals(sop1.isExpedited(), popRetrieved.expedited);
assert(android.content.PeriodicSync.syncExtrasEquals(sop1.extras, popRetrieved.extras));
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index efbfb33..502ee18 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -90,7 +90,6 @@
private static final int MSG_SYSTEM_READY = 3;
private static final int MSG_BOOT_COMPLETED = 4;
private static final int MSG_USER_SWITCHED = 5;
- private static final int MSG_START_ACCESSORY_MODE = 6;
private static final int AUDIO_MODE_NONE = 0;
private static final int AUDIO_MODE_SOURCE = 1;
@@ -153,7 +152,7 @@
mHandler.updateState(state);
} else if ("START".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory start");
- mHandler.sendEmptyMessage(MSG_START_ACCESSORY_MODE);
+ startAccessoryMode();
}
}
};
@@ -171,7 +170,7 @@
if (nativeIsStartRequested()) {
if (DEBUG) Slog.d(TAG, "accessory attached at boot");
- mHandler.sendEmptyMessage(MSG_START_ACCESSORY_MODE);
+ startAccessoryMode();
}
boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
@@ -233,8 +232,6 @@
functions = UsbManager.USB_FUNCTION_AUDIO_SOURCE;
}
- if (DEBUG) Slog.d(TAG, "startAccessoryMode: " + functions);
-
if (functions != null) {
mAccessoryModeRequestTime = SystemClock.elapsedRealtime();
setCurrentFunctions(functions, false);
@@ -309,7 +306,6 @@
// current USB state
private boolean mConnected;
private boolean mConfigured;
- private boolean mAccessoryStartPending;
private String mCurrentFunctions;
private String mDefaultFunctions;
private UsbAccessory mCurrentAccessory;
@@ -538,7 +534,6 @@
if (mConfigured && enteringAccessoryMode) {
// successfully entered accessory mode
- mAccessoryModeRequestTime = 0;
if (mAccessoryStrings != null) {
mCurrentAccessory = new UsbAccessory(mAccessoryStrings);
@@ -616,11 +611,6 @@
case MSG_UPDATE_STATE:
mConnected = (msg.arg1 == 1);
mConfigured = (msg.arg2 == 1);
-
- if (!mConnected) {
- mAccessoryStartPending = false;
- }
-
updateUsbNotification();
updateAdbNotification();
if (containsFunction(mCurrentFunctions,
@@ -634,10 +624,6 @@
updateUsbState();
updateAudioSourceFunction();
}
- if (mConnected && mConfigured && mAccessoryStartPending) {
- startAccessoryMode();
- mAccessoryStartPending = false;
- }
break;
case MSG_ENABLE_ADB:
setAdbEnabled(msg.arg1 == 1);
@@ -674,16 +660,6 @@
mCurrentUser = msg.arg1;
break;
}
- case MSG_START_ACCESSORY_MODE:
- if (mConnected && mConfigured) {
- startAccessoryMode();
- } else {
- // we sometimes receive the kernel "accessory start" uevent
- // before the "configured" uevent. In this case we need to defer
- // handling this event until after we received the configured event
- mAccessoryStartPending = true;
- }
- break;
}
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index aff0088..652998e 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1317,9 +1317,9 @@
curIsFormatted = false;
// Untranslatable strings must only exist in the default [empty] locale
if (locale.size() > 0) {
- fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
- " in locale '%s'\n", String8(name).string(),
- bundle->getResourceSourceDirs()[0],
+ SourcePos(in->getPrintableSource(), block.getLineNumber()).warning(
+ "string '%s' marked untranslatable but exists in locale '%s'\n",
+ String8(name).string(),
locale.string());
// hasErrors = localHasErrors = true;
} else {
@@ -1330,7 +1330,10 @@
// having no default translation.
}
} else {
- outTable->addLocalization(name, locale);
+ outTable->addLocalization(
+ name,
+ locale,
+ SourcePos(in->getPrintableSource(), block.getLineNumber()));
}
if (formatted == false16) {
@@ -2570,9 +2573,9 @@
void
-ResourceTable::addLocalization(const String16& name, const String8& locale)
+ResourceTable::addLocalization(const String16& name, const String8& locale, const SourcePos& src)
{
- mLocalizations[name].insert(locale);
+ mLocalizations[name][locale] = src;
}
@@ -2592,21 +2595,22 @@
const String8 defaultLocale;
// For all strings...
- for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
+ for (map<String16, map<String8, SourcePos> >::iterator nameIter = mLocalizations.begin();
nameIter != mLocalizations.end();
nameIter++) {
- const set<String8>& configSet = nameIter->second; // naming convenience
+ const map<String8, SourcePos>& configSrcMap = nameIter->second;
// Look for strings with no default localization
- if (configSet.count(defaultLocale) == 0) {
- fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
- String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
- for (set<String8>::const_iterator locales = configSet.begin();
- locales != configSet.end();
- locales++) {
- fprintf(stdout, " %s", (*locales).string());
+ if (configSrcMap.count(defaultLocale) == 0) {
+ SourcePos().warning("string '%s' has no default translation.",
+ String8(nameIter->first).string());
+ if (mBundle->getVerbose()) {
+ for (map<String8, SourcePos>::const_iterator locales = configSrcMap.begin();
+ locales != configSrcMap.end();
+ locales++) {
+ locales->second.printf("locale %s found", locales->first.string());
+ }
}
- fprintf(stdout, "\n");
// !!! TODO: throw an error here in some circumstances
}
@@ -2616,6 +2620,8 @@
const char* start = allConfigs;
const char* comma;
+ set<String8> missingConfigs;
+ AaptLocaleValue locale;
do {
String8 config;
comma = strchr(start, ',');
@@ -2626,27 +2632,38 @@
config.setTo(start);
}
+ if (!locale.initFromFilterString(config)) {
+ continue;
+ }
+
// don't bother with the pseudolocale "zz_ZZ"
if (config != "zz_ZZ") {
- if (configSet.find(config) == configSet.end()) {
+ if (configSrcMap.find(config) == configSrcMap.end()) {
// okay, no specific localization found. it's possible that we are
// requiring a specific regional localization [e.g. de_DE] but there is an
// available string in the generic language localization [e.g. de];
// consider that string to have fulfilled the localization requirement.
String8 region(config.string(), 2);
- if (configSet.find(region) == configSet.end()) {
- if (configSet.count(defaultLocale) == 0) {
- fprintf(stdout, "aapt: warning: "
- "**** string '%s' has no default or required localization "
- "for '%s' in %s\n",
- String8(nameIter->first).string(),
- config.string(),
- mBundle->getResourceSourceDirs()[0]);
- }
+ if (configSrcMap.find(region) == configSrcMap.end() &&
+ configSrcMap.count(defaultLocale) == 0) {
+ missingConfigs.insert(config);
}
}
}
- } while (comma != NULL);
+ } while (comma != NULL);
+
+ if (!missingConfigs.empty()) {
+ String8 configStr;
+ for (set<String8>::iterator iter = missingConfigs.begin();
+ iter != missingConfigs.end();
+ iter++) {
+ configStr.appendFormat(" %s", iter->string());
+ }
+ SourcePos().warning("string '%s' is missing %u required localizations:%s",
+ String8(nameIter->first).string(),
+ (unsigned int)missingConfigs.size(),
+ configStr.string());
+ }
}
}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index a3e0666..75005cd 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -220,7 +220,7 @@
status_t assignResourceIds();
status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL);
- void addLocalization(const String16& name, const String8& locale);
+ void addLocalization(const String16& name, const String8& locale, const SourcePos& src);
status_t validateLocalizations(void);
status_t flatten(Bundle*, const sp<AaptFile>& dest);
@@ -551,7 +551,7 @@
Bundle* mBundle;
// key = string resource name, value = set of locales in which that name is defined
- map<String16, set<String8> > mLocalizations;
+ map<String16, map<String8, SourcePos> > mLocalizations;
};
#endif
diff --git a/tools/aapt/SourcePos.cpp b/tools/aapt/SourcePos.cpp
index e2a921c..ae25047 100644
--- a/tools/aapt/SourcePos.cpp
+++ b/tools/aapt/SourcePos.cpp
@@ -10,17 +10,20 @@
// =============================================================================
struct ErrorPos
{
+ enum Level {
+ NOTE,
+ WARNING,
+ ERROR
+ };
+
String8 file;
int line;
String8 error;
- bool fatal;
+ Level level;
ErrorPos();
ErrorPos(const ErrorPos& that);
- ErrorPos(const String8& file, int line, const String8& error, bool fatal);
- ~ErrorPos();
- bool operator<(const ErrorPos& rhs) const;
- bool operator==(const ErrorPos& rhs) const;
+ ErrorPos(const String8& file, int line, const String8& error, Level level);
ErrorPos& operator=(const ErrorPos& rhs);
void print(FILE* to) const;
@@ -29,7 +32,7 @@
static vector<ErrorPos> g_errors;
ErrorPos::ErrorPos()
- :line(-1), fatal(false)
+ :line(-1), level(NOTE)
{
}
@@ -37,61 +40,52 @@
:file(that.file),
line(that.line),
error(that.error),
- fatal(that.fatal)
+ level(that.level)
{
}
-ErrorPos::ErrorPos(const String8& f, int l, const String8& e, bool fat)
+ErrorPos::ErrorPos(const String8& f, int l, const String8& e, Level lev)
:file(f),
line(l),
error(e),
- fatal(fat)
+ level(lev)
{
}
-ErrorPos::~ErrorPos()
-{
-}
-
-bool
-ErrorPos::operator<(const ErrorPos& rhs) const
-{
- if (this->file < rhs.file) return true;
- if (this->file == rhs.file) {
- if (this->line < rhs.line) return true;
- if (this->line == rhs.line) {
- if (this->error < rhs.error) return true;
- }
- }
- return false;
-}
-
-bool
-ErrorPos::operator==(const ErrorPos& rhs) const
-{
- return this->file == rhs.file
- && this->line == rhs.line
- && this->error == rhs.error;
-}
-
ErrorPos&
ErrorPos::operator=(const ErrorPos& rhs)
{
this->file = rhs.file;
this->line = rhs.line;
this->error = rhs.error;
+ this->level = rhs.level;
return *this;
}
void
ErrorPos::print(FILE* to) const
{
- const char* type = fatal ? "error:" : "warning:";
+ const char* type = "";
+ switch (level) {
+ case NOTE:
+ type = "note: ";
+ break;
+ case WARNING:
+ type = "warning: ";
+ break;
+ case ERROR:
+ type = "error: ";
+ break;
+ }
- if (this->line >= 0) {
- fprintf(to, "%s:%d: %s %s\n", this->file.string(), this->line, type, this->error.string());
+ if (!this->file.isEmpty()) {
+ if (this->line >= 0) {
+ fprintf(to, "%s:%d: %s%s\n", this->file.string(), this->line, type, this->error.string());
+ } else {
+ fprintf(to, "%s: %s%s\n", this->file.string(), type, this->error.string());
+ }
} else {
- fprintf(to, "%s: %s %s\n", this->file.string(), type, this->error.string());
+ fprintf(to, "%s%s\n", type, this->error.string());
}
}
@@ -116,40 +110,34 @@
{
}
-int
+void
SourcePos::error(const char* fmt, ...) const
{
- int retval=0;
- char buf[1024];
va_list ap;
va_start(ap, fmt);
- retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+ String8 msg = String8::formatV(fmt, ap);
va_end(ap);
- char* p = buf + retval - 1;
- while (p > buf && *p == '\n') {
- *p = '\0';
- p--;
- }
- g_errors.push_back(ErrorPos(this->file, this->line, String8(buf), true));
- return retval;
+ g_errors.push_back(ErrorPos(this->file, this->line, msg, ErrorPos::ERROR));
}
-int
+void
SourcePos::warning(const char* fmt, ...) const
{
- int retval=0;
- char buf[1024];
va_list ap;
va_start(ap, fmt);
- retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+ String8 msg = String8::formatV(fmt, ap);
va_end(ap);
- char* p = buf + retval - 1;
- while (p > buf && *p == '\n') {
- *p = '\0';
- p--;
- }
- ErrorPos(this->file, this->line, String8(buf), false).print(stderr);
- return retval;
+ ErrorPos(this->file, this->line, msg, ErrorPos::WARNING).print(stderr);
+}
+
+void
+SourcePos::printf(const char* fmt, ...) const
+{
+ va_list ap;
+ va_start(ap, fmt);
+ String8 msg = String8::formatV(fmt, ap);
+ va_end(ap);
+ ErrorPos(this->file, this->line, msg, ErrorPos::NOTE).print(stderr);
}
bool
diff --git a/tools/aapt/SourcePos.h b/tools/aapt/SourcePos.h
index 33f72a9..4ce817f 100644
--- a/tools/aapt/SourcePos.h
+++ b/tools/aapt/SourcePos.h
@@ -17,8 +17,9 @@
SourcePos();
~SourcePos();
- int error(const char* fmt, ...) const;
- int warning(const char* fmt, ...) const;
+ void error(const char* fmt, ...) const;
+ void warning(const char* fmt, ...) const;
+ void printf(const char* fmt, ...) const;
static bool hasErrors();
static void printErrors(FILE* to);