Merge "Do not reset language in case of the TTS service reconnect."
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..4c1f6fa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3156,6 +3156,8 @@
method public final void setProgressBarIndeterminate(boolean);
method public final void setProgressBarIndeterminateVisibility(boolean);
method public final void setProgressBarVisibility(boolean);
+ method public void setRecentsIcon(android.graphics.Bitmap);
+ method public void setRecentsLabel(java.lang.CharSequence);
method public void setRequestedOrientation(int);
method public final void setResult(int);
method public final void setResult(int, android.content.Intent);
@@ -3276,6 +3278,8 @@
method public void readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
+ field public android.graphics.Bitmap activityIcon;
+ field public java.lang.CharSequence activityLabel;
field public android.content.Intent baseIntent;
field public java.lang.CharSequence description;
field public int id;
@@ -4202,6 +4206,20 @@
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_RECOMMENDATION = "recommendation";
+ 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 +4264,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 +4325,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 +11801,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/Activity.java b/core/java/android/app/Activity.java
index 606d803..e38bbb3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4701,6 +4701,47 @@
}
/**
+ * Set a label to be used in the Recents task display. The activities of a task are traversed
+ * in order from the topmost activity to the bottommost. As soon as one activity returns a
+ * non-null Recents label the traversal is ended and that value will be used in
+ * {@link ActivityManager.RecentTaskInfo#activityLabel}
+ *
+ * @see ActivityManager#getRecentTasks
+ *
+ * @param recentsLabel The label to use in the RecentTaskInfo.
+ */
+ public void setRecentsLabel(CharSequence recentsLabel) {
+ try {
+ ActivityManagerNative.getDefault().setRecentsLabel(mToken, recentsLabel);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Set an icon to be used in the Recents task display. The activities of a task are traversed
+ * in order from the topmost activity to the bottommost. As soon as one activity returns a
+ * non-null Recents icon the traversal is ended and that value will be used in
+ * {@link ActivityManager.RecentTaskInfo#activityIcon}.
+ *
+ * @see ActivityManager#getRecentTasks
+ *
+ * @param recentsIcon The Bitmap to use in the RecentTaskInfo.
+ */
+ public void setRecentsIcon(Bitmap recentsIcon) {
+ final Bitmap scaledIcon;
+ if (recentsIcon != null) {
+ final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
+ scaledIcon = Bitmap.createScaledBitmap(recentsIcon, size, size, true);
+ } else {
+ scaledIcon = null;
+ }
+ try {
+ ActivityManagerNative.getDefault().setRecentsIcon(mToken, scaledIcon);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Sets the visibility of the progress bar in the title.
* <p>
* In order for the progress bar to be shown, the feature must be requested
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 0787ef1..d386eff 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -510,11 +510,23 @@
public int stackId;
/**
- * The id the of the user the task was running as.
+ * The id of the user the task was running as.
* @hide
*/
public int userId;
+ /**
+ * The label of the highest activity in the task stack to have set a label
+ * {@link Activity#setRecentsLabel}.
+ */
+ public CharSequence activityLabel;
+
+ /**
+ * The Bitmap icon of the highest activity in the task stack to set a Bitmap using
+ * {@link Activity#setRecentsIcon}.
+ */
+ public Bitmap activityIcon;
+
public RecentTaskInfo() {
}
@@ -536,6 +548,14 @@
ComponentName.writeToParcel(origActivity, dest);
TextUtils.writeToParcel(description, dest,
Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ TextUtils.writeToParcel(activityLabel, dest,
+ Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ if (activityIcon == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ activityIcon.writeToParcel(dest, 0);
+ }
dest.writeInt(stackId);
dest.writeInt(userId);
}
@@ -550,6 +570,8 @@
}
origActivity = ComponentName.readFromParcel(source);
description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ activityLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ activityIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
stackId = source.readInt();
userId = source.readInt();
}
@@ -1970,7 +1992,11 @@
* @return dimensions of square icons in terms of pixels
*/
public int getLauncherLargeIconSize() {
- final Resources res = mContext.getResources();
+ return getLauncherLargeIconSizeInner(mContext);
+ }
+
+ static int getLauncherLargeIconSizeInner(Context context) {
+ final Resources res = context.getResources();
final int size = res.getDimensionPixelSize(android.R.dimen.app_icon_size);
final int sw = res.getConfiguration().smallestScreenWidthDp;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 373a8a3..707a038 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2128,6 +2128,25 @@
reply.writeInt(isInLockTaskMode ? 1 : 0);
return true;
}
+
+ case SET_RECENTS_LABEL_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ CharSequence recentsLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
+ setRecentsLabel(token, recentsLabel);
+ reply.writeNoException();
+ return true;
+ }
+
+ case SET_RECENTS_ICON_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ Bitmap recentsIcon = data.readInt() != 0
+ ? Bitmap.CREATOR.createFromParcel(data) : null;
+ setRecentsIcon(token, recentsIcon);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -4899,5 +4918,36 @@
return isInLockTaskMode;
}
+ public void setRecentsLabel(IBinder token, CharSequence recentsLabel) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ TextUtils.writeToParcel(recentsLabel, data, 0);
+ mRemote.transact(SET_RECENTS_LABEL_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void setRecentsIcon(IBinder token, Bitmap recentsBitmap) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ if (recentsBitmap != null) {
+ data.writeInt(1);
+ recentsBitmap.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ mRemote.transact(SET_RECENTS_ICON_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index cb06a42..3b56839 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -436,6 +436,12 @@
/** @hide */
public boolean isInLockTaskMode() throws RemoteException;
+ /** @hide */
+ public void setRecentsLabel(IBinder token, CharSequence recentsLabel) throws RemoteException;
+
+ /** @hide */
+ public void setRecentsIcon(IBinder token, Bitmap recentsBitmap) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -735,4 +741,6 @@
int START_LOCK_TASK_BY_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+214;
int STOP_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+215;
int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216;
+ int SET_RECENTS_LABEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
+ int SET_RECENTS_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 12a8ff6..13e74da 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -444,41 +444,82 @@
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: a specific, timely recommendation for a single thing.
+ * For example, a news app might want to recommend a news story it believes the user will
+ * want to read next.
+ */
+ public static final String CATEGORY_RECOMMENDATION = "recommendation";
+
+ /**
+ * 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 +660,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 +863,7 @@
priority = parcel.readInt();
- kind = parcel.createStringArray(); // may set kind to null
+ category = parcel.readString();
extras = parcel.readBundle(); // may be null
@@ -890,12 +938,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 +1087,7 @@
parcel.writeInt(priority);
- parcel.writeStringArray(kind); // ok for null
+ parcel.writeString(category);
parcel.writeBundle(extras); // null ok
@@ -1180,16 +1223,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 +1305,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 +1713,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 +2042,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/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b0d94d5..87beb95 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -155,6 +155,7 @@
private static final String FOREGROUND_DATA = "fg";
private static final String WAKELOCK_DATA = "wl";
private static final String KERNEL_WAKELOCK_DATA = "kwl";
+ private static final String WAKEUP_REASON_DATA = "wr";
private static final String NETWORK_DATA = "nt";
private static final String USER_ACTIVITY_DATA = "ua";
private static final String BATTERY_DATA = "bt";
@@ -201,6 +202,25 @@
}
/**
+ * State for keeping track of long counting information.
+ */
+ public static abstract class LongCounter {
+
+ /**
+ * Returns the count associated with this Counter for the
+ * selected type of statistics.
+ *
+ * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT
+ */
+ public abstract long getCountLocked(int which);
+
+ /**
+ * Temporary for debugging.
+ */
+ public abstract void logState(Printer pw, String prefix);
+ }
+
+ /**
* State for keeping track of timing information.
*/
public static abstract class Timer {
@@ -552,8 +572,8 @@
// These states always appear directly in the first int token
// of a delta change; they should be ones that change relatively
// frequently.
- public static final int STATE_WAKE_LOCK_FLAG = 1<<31;
- public static final int STATE_SENSOR_ON_FLAG = 1<<30;
+ public static final int STATE_CPU_RUNNING_FLAG = 1<<31;
+ public static final int STATE_WAKE_LOCK_FLAG = 1<<30;
public static final int STATE_GPS_ON_FLAG = 1<<29;
public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<28;
public static final int STATE_WIFI_SCAN_FLAG = 1<<27;
@@ -562,9 +582,9 @@
public static final int STATE_WIFI_RUNNING_FLAG = 1<<24;
// These are on the lower bits used for the command; if they change
// we need to write another int of data.
- public static final int STATE_PHONE_SCANNING_FLAG = 1<<23;
+ public static final int STATE_SENSOR_ON_FLAG = 1<<23;
public static final int STATE_AUDIO_ON_FLAG = 1<<22;
- public static final int STATE_VIDEO_ON_FLAG = 1<<21;
+ public static final int STATE_PHONE_SCANNING_FLAG = 1<<21;
public static final int STATE_SCREEN_ON_FLAG = 1<<20;
public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19;
public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18;
@@ -577,6 +597,7 @@
public int states;
+ public static final int STATE2_VIDEO_ON_FLAG = 1<<0;
public int states2;
// The wake lock that was acquired at this point.
@@ -653,6 +674,7 @@
| ((((int)batteryVoltage)<<16)&0xffff0000);
dest.writeInt(bat);
dest.writeInt(states);
+ dest.writeInt(states2);
if (wakelockTag != null) {
wakelockTag.writeToParcel(dest, flags);
}
@@ -680,6 +702,7 @@
batteryTemperature = (short)(bat2&0xffff);
batteryVoltage = (char)((bat2>>16)&0xffff);
states = src.readInt();
+ states2 = src.readInt();
if ((bat&0x10000000) != 0) {
wakelockTag = localWakelockTag;
wakelockTag.readFromParcel(src);
@@ -718,6 +741,7 @@
batteryTemperature = 0;
batteryVoltage = 0;
states = 0;
+ states2 = 0;
wakelockTag = null;
wakeReasonTag = null;
eventCode = EVENT_NONE;
@@ -744,6 +768,7 @@
batteryTemperature = o.batteryTemperature;
batteryVoltage = o.batteryVoltage;
states = o.states;
+ states2 = o.states2;
if (o.wakelockTag != null) {
wakelockTag = localWakelockTag;
wakelockTag.setTo(o.wakelockTag);
@@ -774,6 +799,7 @@
&& batteryTemperature == o.batteryTemperature
&& batteryVoltage == o.batteryVoltage
&& states == o.states
+ && states2 == o.states2
&& currentTime == o.currentTime;
}
@@ -970,6 +996,15 @@
public abstract int getMobileRadioActiveCount(int which);
/**
+ * Returns the time in microseconds that is the difference between the mobile radio
+ * time we saw based on the elapsed timestamp when going down vs. the given time stamp
+ * from the radio.
+ *
+ * {@hide}
+ */
+ public abstract long getMobileRadioActiveAdjustedTime(int which);
+
+ /**
* Returns the time in microseconds that the mobile network has been active
* (in a high power state) but not being able to blame on an app.
*
@@ -1026,9 +1061,10 @@
* {@hide}
*/
public abstract int getPhoneDataConnectionCount(int dataType, int which);
-
+
public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS
= new BitDescription[] {
+ new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"),
new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor", "s"),
new BitDescription(HistoryItem.STATE_GPS_ON_FLAG, "gps", "g"),
@@ -1039,7 +1075,6 @@
new BitDescription(HistoryItem.STATE_WIFI_RUNNING_FLAG, "wifi_running", "Wr"),
new BitDescription(HistoryItem.STATE_PHONE_SCANNING_FLAG, "phone_scanning", "Psc"),
new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio", "a"),
- new BitDescription(HistoryItem.STATE_VIDEO_ON_FLAG, "video", "v"),
new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"),
new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"),
new BitDescription(HistoryItem.STATE_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
@@ -1062,6 +1097,11 @@
SCREEN_BRIGHTNESS_NAMES, SCREEN_BRIGHTNESS_SHORT_NAMES),
};
+ public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS
+ = new BitDescription[] {
+ new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
+ };
+
public static final String[] HISTORY_EVENT_NAMES = new String[] {
"null", "proc", "fg", "top", "sync"
};
@@ -1294,6 +1334,8 @@
*/
public abstract long computeRealtime(long curTime, int which);
+ public abstract Map<String, ? extends LongCounter> getWakeupReasonStats();
+
public abstract Map<String, ? extends Timer> getKernelWakelockStats();
/** Returns the number of different speeds that the CPU can run at */
@@ -1554,7 +1596,8 @@
wifiRunningTime / 1000, bluetoothOnTime / 1000,
mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes,
fullWakeLockTimeTotal, partialWakeLockTimeTotal,
- getInputEventCount(which), getMobileRadioActiveTime(rawRealtime, which));
+ getInputEventCount(which), getMobileRadioActiveTime(rawRealtime, which),
+ getMobileRadioActiveAdjustedTime(which));
// Dump screen brightness stats
Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -1626,16 +1669,22 @@
}
if (reqUid < 0) {
- Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+ Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
if (kernelWakelocks.size() > 0) {
- for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
+ for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
sb.setLength(0);
printWakeLockCheckin(sb, ent.getValue(), rawRealtime, null, which, "");
-
- dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(),
+ dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(),
sb.toString());
}
}
+ Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+ if (wakeupReasons.size() > 0) {
+ for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
+ dumpLine(pw, 0 /* uid */, category, WAKEUP_REASON_DATA,
+ "\"" + ent.getKey() + "\"", ent.getValue().getCountLocked(which));
+ }
+ }
}
BatteryStatsHelper helper = new BatteryStatsHelper(context);
@@ -2111,6 +2160,18 @@
pw.println(sb.toString());
}
+ final long mobileActiveAdjustedTime = getMobileRadioActiveAdjustedTime(which);
+ if (mobileActiveAdjustedTime != 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Mobile radio active adjusted time: ");
+ formatTimeMs(sb, mobileActiveAdjustedTime / 1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(mobileActiveAdjustedTime, whichBatteryRealtime));
+ sb.append(")");
+ pw.println(sb.toString());
+ }
+
pw.print(prefix);
pw.print(" Wi-Fi total received: "); pw.print(formatBytesLocked(wifiRxTotalBytes));
pw.print(", sent: "); pw.print(formatBytesLocked(wifiTxTotalBytes));
@@ -2346,24 +2407,49 @@
pw.println();
}
}
- }
- if (timers.size() > 0) {
- Collections.sort(timers, timerComparator);
- pw.print(prefix); pw.println(" All partial wake locks:");
- for (int i=0; i<timers.size(); i++) {
- TimerEntry timer = timers.get(i);
- sb.setLength(0);
- sb.append(" Wake lock ");
- UserHandle.formatUid(sb, timer.mId);
- sb.append(" ");
- sb.append(timer.mName);
- printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
- sb.append(" realtime");
- pw.println(sb.toString());
+ if (timers.size() > 0) {
+ Collections.sort(timers, timerComparator);
+ pw.print(prefix); pw.println(" All partial wake locks:");
+ for (int i=0; i<timers.size(); i++) {
+ TimerEntry timer = timers.get(i);
+ sb.setLength(0);
+ sb.append(" Wake lock ");
+ UserHandle.formatUid(sb, timer.mId);
+ sb.append(" ");
+ sb.append(timer.mName);
+ printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
+ sb.append(" realtime");
+ pw.println(sb.toString());
+ }
+ timers.clear();
+ pw.println();
}
- timers.clear();
- pw.println();
+
+ Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+ if (wakeupReasons.size() > 0) {
+ pw.print(prefix); pw.println(" All wakeup reasons:");
+ final ArrayList<TimerEntry> reasons = new ArrayList<TimerEntry>();
+ for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
+ BatteryStats.LongCounter counter = ent.getValue();
+ reasons.add(new TimerEntry(ent.getKey(), 0, null,
+ ent.getValue().getCountLocked(which)));
+ }
+ Collections.sort(reasons, timerComparator);
+ for (int i=0; i<reasons.size(); i++) {
+ TimerEntry timer = reasons.get(i);
+ String linePrefix = ": ";
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Wakeup reason ");
+ sb.append(timer.mName);
+ sb.append(": ");
+ formatTimeMs(sb, timer.mTime);
+ sb.append("realtime");
+ pw.println(sb.toString());
+ }
+ pw.println();
+ }
}
for (int iu=0; iu<NU; iu++) {
@@ -2772,6 +2858,7 @@
public static class HistoryPrinter {
int oldState = 0;
+ int oldState2 = 0;
int oldLevel = -1;
int oldStatus = -1;
int oldHealth = -1;
@@ -2780,7 +2867,8 @@
int oldVolt = -1;
long lastTime = -1;
- public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin) {
+ public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin,
+ boolean verbose) {
if (!checkin) {
pw.print(" ");
if (now >= 0) {
@@ -2831,16 +2919,18 @@
if (rec.batteryLevel < 10) pw.print("00");
else if (rec.batteryLevel < 100) pw.print("0");
pw.print(rec.batteryLevel);
- pw.print(" ");
- if (rec.states < 0) ;
- else if (rec.states < 0x10) pw.print("0000000");
- else if (rec.states < 0x100) pw.print("000000");
- else if (rec.states < 0x1000) pw.print("00000");
- else if (rec.states < 0x10000) pw.print("0000");
- else if (rec.states < 0x100000) pw.print("000");
- else if (rec.states < 0x1000000) pw.print("00");
- else if (rec.states < 0x10000000) pw.print("0");
- pw.print(Integer.toHexString(rec.states));
+ if (verbose) {
+ pw.print(" ");
+ if (rec.states < 0) ;
+ else if (rec.states < 0x10) pw.print("0000000");
+ else if (rec.states < 0x100) pw.print("000000");
+ else if (rec.states < 0x1000) pw.print("00000");
+ else if (rec.states < 0x10000) pw.print("0000");
+ else if (rec.states < 0x100000) pw.print("000");
+ else if (rec.states < 0x1000000) pw.print("00");
+ else if (rec.states < 0x10000000) pw.print("0");
+ pw.print(Integer.toHexString(rec.states));
+ }
} else {
if (oldLevel != rec.batteryLevel) {
oldLevel = rec.batteryLevel;
@@ -2934,6 +3024,8 @@
}
printBitDescriptions(pw, oldState, rec.states, rec.wakelockTag,
HISTORY_STATE_DESCRIPTIONS, !checkin);
+ printBitDescriptions(pw, oldState2, rec.states2, null,
+ HISTORY_STATE2_DESCRIPTIONS, !checkin);
if (rec.wakeReasonTag != null) {
if (checkin) {
pw.print(",Wr=");
@@ -3010,6 +3102,7 @@
public static final int DUMP_CHARGED_ONLY = 1<<1;
public static final int DUMP_HISTORY_ONLY = 1<<2;
public static final int DUMP_INCLUDE_HISTORY = 1<<3;
+ public static final int DUMP_VERBOSE = 1<<4;
/**
* Dumps a human-readable summary of the battery statistics to the given PrintWriter.
@@ -3047,7 +3140,8 @@
while (getNextHistoryLocked(rec)) {
lastTime = rec.time;
if (rec.time >= histStart) {
- hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false);
+ hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false,
+ (flags&DUMP_VERBOSE) != 0);
}
}
if (histStart >= 0) {
@@ -3064,7 +3158,7 @@
pw.println("Old battery History:");
HistoryPrinter hprinter = new HistoryPrinter();
while (getNextOldHistoryLocked(rec)) {
- hprinter.printNextItem(pw, rec, now, false);
+ hprinter.printNextItem(pw, rec, now, false, (flags&DUMP_VERBOSE) != 0);
}
pw.println();
} finally {
@@ -3150,7 +3244,7 @@
if (rec.time >= histStart) {
pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
pw.print(HISTORY_DATA); pw.print(',');
- hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true);
+ hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true, false);
}
}
if (histStart >= 0) {
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/View.java b/core/java/android/view/View.java
index a8ccd49..c5835da 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4210,7 +4210,7 @@
out.append(" #");
out.append(Integer.toHexString(id));
final Resources r = mResources;
- if (id != 0 && r != null) {
+ if (Resources.resourceHasPackage(id) && r != null) {
try {
String pkgname;
switch (id&0xff000000) {
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/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 5ba5c57..32f700d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -55,7 +55,7 @@
void noteScreenOff();
void noteInputEvent();
void noteUserActivity(int uid, int event);
- void noteDataConnectionActive(int type, boolean active);
+ void noteDataConnectionActive(int type, boolean active, long timestampNs);
void notePhoneOn();
void notePhoneOff();
void notePhoneSignalStrength(in SignalStrength signalStrength);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 082f1a5..1d88533 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -87,7 +87,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 99 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 101 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -191,7 +191,7 @@
long mHistoryBaseTime;
boolean mHaveBatteryLevel = false;
- boolean mRecordingHistory = true;
+ boolean mRecordingHistory = false;
int mNumHistoryItems;
static final int MAX_HISTORY_BUFFER = 128*1024; // 128KB
@@ -200,6 +200,7 @@
final HistoryItem mHistoryLastWritten = new HistoryItem();
final HistoryItem mHistoryLastLastWritten = new HistoryItem();
final HistoryItem mHistoryReadTmp = new HistoryItem();
+ final HistoryItem mHistoryAddTmp = new HistoryItem();
final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<HistoryTag, Integer>();
String[] mReadHistoryStrings;
int[] mReadHistoryUids;
@@ -209,7 +210,8 @@
int mHistoryBufferLastPos = -1;
boolean mHistoryOverflow = false;
long mLastHistoryElapsedRealtime = 0;
- long mLastHistoryUptime = 0;
+ long mTrackRunningHistoryElapsedRealtime = 0;
+ long mTrackRunningHistoryUptime = 0;
final HistoryItem mHistoryCur = new HistoryItem();
@@ -287,6 +289,7 @@
boolean mMobileRadioActive;
StopwatchTimer mMobileRadioActiveTimer;
StopwatchTimer mMobileRadioActivePerAppTimer;
+ LongSamplingCounter mMobileRadioActiveAdjustedTime;
LongSamplingCounter mMobileRadioActiveUnknownTime;
LongSamplingCounter mMobileRadioActiveUnknownCount;
@@ -330,12 +333,21 @@
private final HashMap<String, SamplingTimer> mKernelWakelockStats =
new HashMap<String, SamplingTimer>();
- public Map<String, ? extends SamplingTimer> getKernelWakelockStats() {
+ public Map<String, ? extends Timer> getKernelWakelockStats() {
return mKernelWakelockStats;
}
private static int sKernelWakelockUpdateVersion = 0;
+ String mLastWakeupReason = null;
+ long mLastWakeupUptimeMs = 0;
+ private final HashMap<String, LongSamplingCounter> mWakeupReasonStats =
+ new HashMap<String, LongSamplingCounter>();
+
+ public Map<String, ? extends LongCounter> getWakeupReasonStats() {
+ return mWakeupReasonStats;
+ }
+
private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name
Process.PROC_QUOTES,
@@ -719,7 +731,7 @@
}
}
- public static class LongSamplingCounter implements TimeBaseObs {
+ public static class LongSamplingCounter extends LongCounter implements TimeBaseObs {
final TimeBase mTimeBase;
long mCount;
long mLoadedCount;
@@ -775,6 +787,14 @@
return val;
}
+ @Override
+ public void logState(Printer pw, String prefix) {
+ pw.println(prefix + "mCount=" + mCount
+ + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
+ + " mUnpluggedCount=" + mUnpluggedCount
+ + " mPluggedCount=" + mPluggedCount);
+ }
+
void addCountLocked(long count) {
mCount += count;
}
@@ -1409,6 +1429,10 @@
return 0;
}
+ long getLastUpdateTimeMs() {
+ return mUpdateTime;
+ }
+
void stopRunningLocked(long elapsedRealtimeMs) {
// Ignore attempt to stop a timer that isn't running
if (mNesting == 0) {
@@ -1502,6 +1526,19 @@
}
}
+ /*
+ * Get the wakeup reason counter, and create a new one if one
+ * doesn't already exist.
+ */
+ public LongSamplingCounter getWakeupReasonCounterLocked(String name) {
+ LongSamplingCounter counter = mWakeupReasonStats.get(name);
+ if (counter == null) {
+ counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
+ mWakeupReasonStats.put(name, counter);
+ }
+ return counter;
+ }
+
private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
FileInputStream is;
@@ -1759,6 +1796,10 @@
if (stateIntChanged) {
firstToken |= DELTA_STATE_FLAG;
}
+ final boolean state2IntChanged = cur.states2 != last.states2;
+ if (state2IntChanged) {
+ firstToken |= DELTA_STATE2_FLAG;
+ }
if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
firstToken |= DELTA_WAKELOCK_FLAG;
}
@@ -1795,6 +1836,11 @@
+ " batteryPlugType=" + cur.batteryPlugType
+ " states=0x" + Integer.toHexString(cur.states));
}
+ if (state2IntChanged) {
+ dest.writeInt(cur.states2);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: states2=0x"
+ + Integer.toHexString(cur.states2));
+ }
if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
int wakeLockIndex;
int wakeReasonIndex;
@@ -1917,6 +1963,12 @@
cur.states = (firstToken&DELTA_STATE_MASK) | (cur.states&(~DELTA_STATE_MASK));
}
+ if ((firstToken&DELTA_STATE2_FLAG) != 0) {
+ cur.states2 = src.readInt();
+ if (DEBUG) Slog.i(TAG, "READ DELTA: states2=0x"
+ + Integer.toHexString(cur.states2));
+ }
+
if ((firstToken&DELTA_WAKELOCK_FLAG) != 0) {
int indexes = src.readInt();
int wakeLockIndex = indexes&0xffff;
@@ -1958,29 +2010,34 @@
}
}
- void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs) {
+ void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
final long timeDiff = (mHistoryBaseTime+elapsedRealtimeMs) - mHistoryLastWritten.time;
- final int diffStates = mHistoryLastWritten.states^mHistoryCur.states;
+ final int diffStates = mHistoryLastWritten.states^cur.states;
+ final int diffStates2 = mHistoryLastWritten.states2^cur.states2;
final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
+ final int lastDiffStates2 = mHistoryLastWritten.states2^mHistoryLastLastWritten.states2;
if (DEBUG) Slog.i(TAG, "ADD: tdelta=" + timeDiff + " diff="
+ Integer.toHexString(diffStates) + " lastDiff="
- + Integer.toHexString(lastDiffStates));
+ + Integer.toHexString(lastDiffStates) + " diff2="
+ + Integer.toHexString(diffStates2) + " lastDiff2="
+ + Integer.toHexString(lastDiffStates2));
if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
&& timeDiff < 1000 && (diffStates&lastDiffStates) == 0
- && (mHistoryLastWritten.wakelockTag == null || mHistoryCur.wakelockTag == null)
- && (mHistoryLastWritten.wakeReasonTag == null || mHistoryCur.wakeReasonTag == null)
+ && (diffStates2&lastDiffStates2) == 0
+ && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
+ && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
&& (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
- || mHistoryCur.eventCode == HistoryItem.EVENT_NONE)
- && mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel
- && mHistoryLastWritten.batteryStatus == mHistoryCur.batteryStatus
- && mHistoryLastWritten.batteryHealth == mHistoryCur.batteryHealth
- && mHistoryLastWritten.batteryPlugType == mHistoryCur.batteryPlugType
- && mHistoryLastWritten.batteryTemperature == mHistoryCur.batteryTemperature
- && mHistoryLastWritten.batteryVoltage == mHistoryCur.batteryVoltage) {
+ || cur.eventCode == HistoryItem.EVENT_NONE)
+ && mHistoryLastWritten.batteryLevel == cur.batteryLevel
+ && mHistoryLastWritten.batteryStatus == cur.batteryStatus
+ && mHistoryLastWritten.batteryHealth == cur.batteryHealth
+ && mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
+ && mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
+ && mHistoryLastWritten.batteryVoltage == cur.batteryVoltage) {
// We can merge this new change in with the last one. Merging is
// allowed as long as only the states have changed, and within those states
// as long as no bit has changed both between now and the last entry, as
@@ -1994,23 +2051,23 @@
// Note that the condition above made sure that we aren't in a case where
// both it and the current history item have a wakelock tag.
if (mHistoryLastWritten.wakelockTag != null) {
- mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
- mHistoryCur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
+ cur.wakelockTag = cur.localWakelockTag;
+ cur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
}
// If the last written history had a wake reason tag, we need to retain it.
// Note that the condition above made sure that we aren't in a case where
// both it and the current history item have a wakelock tag.
if (mHistoryLastWritten.wakeReasonTag != null) {
- mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
- mHistoryCur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
+ cur.wakeReasonTag = cur.localWakeReasonTag;
+ cur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
}
// If the last written history had an event, we need to retain it.
// Note that the condition above made sure that we aren't in a case where
// both it and the current history item have an event.
if (mHistoryLastWritten.eventCode != HistoryItem.EVENT_NONE) {
- mHistoryCur.eventCode = mHistoryLastWritten.eventCode;
- mHistoryCur.eventTag = mHistoryCur.localEventTag;
- mHistoryCur.eventTag.setTo(mHistoryLastWritten.eventTag);
+ cur.eventCode = mHistoryLastWritten.eventCode;
+ cur.eventTag = cur.localEventTag;
+ cur.eventTag.setTo(mHistoryLastWritten.eventTag);
}
mHistoryLastWritten.setTo(mHistoryLastLastWritten);
}
@@ -2019,8 +2076,8 @@
if (dataSize >= MAX_HISTORY_BUFFER) {
if (!mHistoryOverflow) {
mHistoryOverflow = true;
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
return;
}
@@ -2028,43 +2085,64 @@
// record changes to the battery level and the most interesting states.
// Once we've reached the maximum maximum number of items, we only
// record changes to the battery level.
- if (mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel &&
+ if (mHistoryLastWritten.batteryLevel == cur.batteryLevel &&
(dataSize >= MAX_MAX_HISTORY_BUFFER
- || ((mHistoryLastWritten.states^mHistoryCur.states)
+ || ((mHistoryLastWritten.states^cur.states)
& HistoryItem.MOST_INTERESTING_STATES) == 0)) {
return;
}
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
return;
}
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
}
- private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
+ private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd,
+ HistoryItem cur) {
if (mIteratingHistory) {
throw new IllegalStateException("Can't do this while iterating history!");
}
mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
mHistoryLastLastWritten.setTo(mHistoryLastWritten);
- mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, mHistoryCur);
+ mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
mLastHistoryElapsedRealtime = elapsedRealtimeMs;
- mLastHistoryUptime = uptimeMs;
- mHistoryCur.wakelockTag = null;
- mHistoryCur.wakeReasonTag = null;
- mHistoryCur.eventCode = HistoryItem.EVENT_NONE;
- mHistoryCur.eventTag = null;
+ cur.wakelockTag = null;
+ cur.wakeReasonTag = null;
+ cur.eventCode = HistoryItem.EVENT_NONE;
+ cur.eventTag = null;
if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
+ " now " + mHistoryBuffer.dataPosition()
+ " size is now " + mHistoryBuffer.dataSize());
}
int mChangedStates = 0;
+ int mChangedStates2 = 0;
void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
+ if (mTrackRunningHistoryElapsedRealtime != 0) {
+ final long diffElapsed = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtime;
+ final long diffUptime = uptimeMs - mTrackRunningHistoryUptime;
+ if (diffUptime < (diffElapsed-20)) {
+ final long wakeElapsedTime = elapsedRealtimeMs - (diffElapsed - diffUptime);
+ mHistoryAddTmp.setTo(mHistoryLastWritten);
+ mHistoryAddTmp.wakelockTag = null;
+ mHistoryAddTmp.wakeReasonTag = null;
+ mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
+ mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
+ addHistoryRecordInnerLocked(wakeElapsedTime, uptimeMs, mHistoryAddTmp);
+ }
+ }
+ mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
+ mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs;
+ mTrackRunningHistoryUptime = uptimeMs;
+ addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
+ }
+
+ void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
if (!USE_OLD_HISTORY) {
return;
@@ -2080,25 +2158,28 @@
// into one record.
if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
&& (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+1000)
- && ((mHistoryEnd.states^mHistoryCur.states)&mChangedStates) == 0) {
+ && ((mHistoryEnd.states^cur.states)&mChangedStates) == 0
+ && ((mHistoryEnd.states2^cur.states2)&mChangedStates2) == 0) {
// If the current is the same as the one before, then we no
// longer need the entry.
if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
&& (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+500)
- && mHistoryLastEnd.sameNonEvent(mHistoryCur)) {
+ && mHistoryLastEnd.sameNonEvent(cur)) {
mHistoryLastEnd.next = null;
mHistoryEnd.next = mHistoryCache;
mHistoryCache = mHistoryEnd;
mHistoryEnd = mHistoryLastEnd;
mHistoryLastEnd = null;
} else {
- mChangedStates |= mHistoryEnd.states^mHistoryCur.states;
- mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur);
+ mChangedStates |= mHistoryEnd.states^cur.states;
+ mChangedStates2 |= mHistoryEnd.states^cur.states2;
+ mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, cur);
}
return;
}
mChangedStates = 0;
+ mChangedStates2 = 0;
if (mNumHistoryItems == MAX_HISTORY_ITEMS
|| mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
@@ -2111,9 +2192,9 @@
// Once we've reached the maximum maximum number of items, we only
// record changes to the battery level.
if (mHistoryEnd != null && mHistoryEnd.batteryLevel
- == mHistoryCur.batteryLevel &&
+ == cur.batteryLevel &&
(mNumHistoryItems >= MAX_MAX_HISTORY_ITEMS
- || ((mHistoryEnd.states^mHistoryCur.states)
+ || ((mHistoryEnd.states^cur.states)
& HistoryItem.MOST_INTERESTING_STATES) == 0)) {
return;
}
@@ -2128,17 +2209,18 @@
mHistoryCur.eventTag = mHistoryCur.localEventTag;
mHistoryCur.eventTag.string = name;
mHistoryCur.eventTag.uid = uid;
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
+ // XXX should be calling addHistoryRecordLocked()?
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
}
- void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
+ void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd, HistoryItem cur) {
HistoryItem rec = mHistoryCache;
if (rec != null) {
mHistoryCache = rec.next;
} else {
rec = new HistoryItem();
}
- rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, mHistoryCur);
+ rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
addHistoryRecordLocked(rec);
}
@@ -2168,7 +2250,8 @@
mHistoryBaseTime = 0;
mLastHistoryElapsedRealtime = 0;
- mLastHistoryUptime = 0;
+ mTrackRunningHistoryElapsedRealtime = 0;
+ mTrackRunningHistoryUptime = 0;
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
@@ -2284,6 +2367,7 @@
if (type == WAKE_TYPE_PARTIAL) {
// Only care about partial wake locks, since full wake locks
// will be canceled when the user puts the screen to sleep.
+ aggregateLastWakeupUptimeLocked(uptime);
if (mWakeLockNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
@@ -2372,14 +2456,26 @@
}
}
- public void noteWakeupReasonLocked(int irq, String reason) {
+ void aggregateLastWakeupUptimeLocked(long uptimeMs) {
+ if (mLastWakeupReason != null) {
+ long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
+ LongSamplingCounter timer = getWakeupReasonCounterLocked(mLastWakeupReason);
+ timer.addCountLocked(deltaUptime);
+ mLastWakeupReason = null;
+ }
+ }
+
+ public void noteWakeupReasonLocked(String reason) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason irq #" + irq + "\"" + reason +"\": "
+ if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason reason \"" + reason +"\": "
+ Integer.toHexString(mHistoryCur.states));
+ aggregateLastWakeupUptimeLocked(uptime);
mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
mHistoryCur.wakeReasonTag.string = reason;
- mHistoryCur.wakeReasonTag.uid = irq;
+ mHistoryCur.wakeReasonTag.uid = 0;
+ mLastWakeupReason = reason;
+ mLastWakeupUptimeMs = uptime;
addHistoryRecordLocked(elapsedRealtime, uptime);
}
@@ -2670,13 +2766,28 @@
}
}
- public void noteDataConnectionActive(int type, boolean active) {
+ public void noteDataConnectionActive(int type, boolean active, long timestampNs) {
if (ConnectivityManager.isNetworkTypeMobile(type)) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
if (mMobileRadioActive != active) {
- if (active) mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
- else mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+ long realElapsedRealtimeMs;
+ if (active) {
+ realElapsedRealtimeMs = elapsedRealtime;
+ mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+ } else {
+ realElapsedRealtimeMs = timestampNs / (1000*1000);
+ long lastUpdateTimeMs = mMobileRadioActiveTimer.getLastUpdateTimeMs();
+ if (realElapsedRealtimeMs < lastUpdateTimeMs) {
+ Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
+ + " is before start time " + lastUpdateTimeMs);
+ realElapsedRealtimeMs = elapsedRealtime;
+ } else if (realElapsedRealtimeMs < elapsedRealtime) {
+ mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtime
+ - realElapsedRealtimeMs);
+ }
+ mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+ }
if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -2685,9 +2796,9 @@
mMobileRadioActiveTimer.startRunningLocked(elapsedRealtime);
mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
} else {
- updateNetworkActivityLocked(NET_UPDATE_MOBILE, elapsedRealtime);
- mMobileRadioActiveTimer.stopRunningLocked(elapsedRealtime);
- mMobileRadioActivePerAppTimer.stopRunningLocked(elapsedRealtime);
+ mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
+ mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
+ updateNetworkActivityLocked(NET_UPDATE_MOBILE, realElapsedRealtimeMs);
}
}
}
@@ -2978,7 +3089,7 @@
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
if (!mVideoOn) {
- mHistoryCur.states |= HistoryItem.STATE_VIDEO_ON_FLAG;
+ mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -2993,7 +3104,7 @@
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
if (mVideoOn) {
- mHistoryCur.states &= ~HistoryItem.STATE_VIDEO_ON_FLAG;
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -3398,6 +3509,10 @@
return mMobileRadioActiveTimer.getCountLocked(which);
}
+ @Override public long getMobileRadioActiveAdjustedTime(int which) {
+ return mMobileRadioActiveAdjustedTime.getCountLocked(which);
+ }
+
@Override public long getMobileRadioActiveUnknownTime(int which) {
return mMobileRadioActiveUnknownTime.getCountLocked(which);
}
@@ -5458,6 +5573,7 @@
}
mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase);
mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase);
+ mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase);
mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase);
mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase);
mWifiOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
@@ -5543,9 +5659,9 @@
PrintWriter pw = new FastPrintWriter(new LogWriter(android.util.Log.WARN, TAG));
pw.println("Histories differ!");
pw.println("Old history:");
- (new HistoryPrinter()).printNextItem(pw, out, now, false);
+ (new HistoryPrinter()).printNextItem(pw, out, now, false, true);
pw.println("New history:");
- (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now, false);
+ (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now, false, true);
pw.flush();
}
}
@@ -5719,6 +5835,7 @@
}
mMobileRadioActiveTimer.reset(false);
mMobileRadioActivePerAppTimer.reset(false);
+ mMobileRadioActiveAdjustedTime.reset(false);
mMobileRadioActiveUnknownTime.reset(false);
mMobileRadioActiveUnknownCount.reset(false);
mWifiOnTimer.reset(false);
@@ -5744,7 +5861,14 @@
}
mKernelWakelockStats.clear();
}
-
+
+ if (mWakeupReasonStats.size() > 0) {
+ for (LongSamplingCounter timer : mWakeupReasonStats.values()) {
+ mOnBatteryScreenOffTimeBase.remove(timer);
+ }
+ mWakeupReasonStats.clear();
+ }
+
initDischarge();
clearHistoryLocked();
@@ -5828,9 +5952,10 @@
mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
+ Integer.toHexString(mHistoryCur.states));
- mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(mSecRealtime, mSecUptime, HistoryItem.CMD_CURRENT_TIME);
- mHistoryCur.currentTime = 0;
+ if (reset) {
+ mRecordingHistory = true;
+ startRecordingHistory(mSecRealtime, mSecUptime, reset);
+ }
addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = level;
if (mScreenOn) {
@@ -5843,9 +5968,6 @@
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
updateTimeBasesLocked(true, !mScreenOn, uptime, realtime);
- if (reset) {
- initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
- }
} else {
pullPendingStateUpdatesLocked();
mHistoryCur.batteryLevel = (byte)level;
@@ -5868,6 +5990,18 @@
}
}
+ private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
+ boolean reset) {
+ mRecordingHistory = true;
+ mHistoryCur.currentTime = System.currentTimeMillis();
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
+ mHistoryCur);
+ mHistoryCur.currentTime = 0;
+ if (reset) {
+ initActiveHistoryEventsLocked(elapsedRealtimeMs, uptimeMs);
+ }
+ }
+
// This should probably be exposed in the API, though it's not critical
private static final int BATTERY_PLUGGED_NONE = 0;
@@ -5895,7 +6029,15 @@
}
if (onBattery) {
mDischargeCurrentLevel = level;
- mRecordingHistory = true;
+ if (!mRecordingHistory) {
+ mRecordingHistory = true;
+ startRecordingHistory(elapsedRealtime, uptime, true);
+ }
+ } else if (level < 96) {
+ if (!mRecordingHistory) {
+ mRecordingHistory = true;
+ startRecordingHistory(elapsedRealtime, uptime, true);
+ }
}
if (onBattery != mOnBattery) {
mHistoryCur.batteryLevel = (byte)level;
@@ -6497,15 +6639,14 @@
}
if (mHistoryBuffer.dataPosition() > 0) {
+ mRecordingHistory = true;
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
if (USE_OLD_HISTORY) {
- addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START);
+ addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
}
- addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START);
- mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_CURRENT_TIME);
- mHistoryCur.currentTime = 0;
+ addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
+ startRecordingHistory(elapsedRealtime, uptime, false);
}
}
@@ -6681,6 +6822,7 @@
mMobileRadioActive = false;
mMobileRadioActiveTimer.readSummaryFromParcelLocked(in);
mMobileRadioActivePerAppTimer.readSummaryFromParcelLocked(in);
+ mMobileRadioActiveAdjustedTime.readSummaryFromParcelLocked(in);
mMobileRadioActiveUnknownTime.readSummaryFromParcelLocked(in);
mMobileRadioActiveUnknownCount.readSummaryFromParcelLocked(in);
mWifiOn = false;
@@ -6708,6 +6850,18 @@
}
}
+ int NWR = in.readInt();
+ if (NWR > 10000) {
+ Slog.w(TAG, "File corrupt: too many wakeup reasons " + NWR);
+ return;
+ }
+ for (int iwr = 0; iwr < NWR; iwr++) {
+ if (in.readInt() != 0) {
+ String reasonName = in.readString();
+ getWakeupReasonCounterLocked(reasonName).readSummaryFromParcelLocked(in);
+ }
+ }
+
sNumSpeedSteps = in.readInt();
if (sNumSpeedSteps < 0 || sNumSpeedSteps > 100) {
throw new BadParcelableException("Bad speed steps in data: " + sNumSpeedSteps);
@@ -6915,6 +7069,7 @@
}
mMobileRadioActiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mMobileRadioActivePerAppTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mMobileRadioActiveAdjustedTime.writeSummaryFromParcelLocked(out);
mMobileRadioActiveUnknownTime.writeSummaryFromParcelLocked(out);
mMobileRadioActiveUnknownCount.writeSummaryFromParcelLocked(out);
mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -6933,7 +7088,19 @@
if (kwlt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
- ent.getValue().writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ kwlt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ out.writeInt(mWakeupReasonStats.size());
+ for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
+ LongSamplingCounter counter = ent.getValue();
+ if (counter != null) {
+ out.writeInt(1);
+ out.writeString(ent.getKey());
+ counter.writeSummaryFromParcelLocked(out);
} else {
out.writeInt(0);
}
@@ -7171,6 +7338,7 @@
mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase, in);
mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase,
in);
+ mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase, in);
mWifiOn = false;
@@ -7205,12 +7373,22 @@
for (int ikw = 0; ikw < NKW; ikw++) {
if (in.readInt() != 0) {
String wakelockName = in.readString();
- in.readInt(); // Extra 0/1 written by Timer.writeTimerToParcel
SamplingTimer kwlt = new SamplingTimer(mOnBatteryTimeBase, in);
mKernelWakelockStats.put(wakelockName, kwlt);
}
}
+ mWakeupReasonStats.clear();
+ int NWR = in.readInt();
+ for (int iwr = 0; iwr < NWR; iwr++) {
+ if (in.readInt() != 0) {
+ String reasonName = in.readString();
+ LongSamplingCounter counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase,
+ in);
+ mWakeupReasonStats.put(reasonName, counter);
+ }
+ }
+
mPartialTimers.clear();
mFullTimers.clear();
mWindowTimers.clear();
@@ -7283,6 +7461,7 @@
}
mMobileRadioActiveTimer.writeToParcel(out, uSecRealtime);
mMobileRadioActivePerAppTimer.writeToParcel(out, uSecRealtime);
+ mMobileRadioActiveAdjustedTime.writeToParcel(out);
mMobileRadioActiveUnknownTime.writeToParcel(out);
mMobileRadioActiveUnknownCount.writeToParcel(out);
mWifiOnTimer.writeToParcel(out, uSecRealtime);
@@ -7313,7 +7492,18 @@
if (kwlt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
- Timer.writeTimerToParcel(out, kwlt, uSecRealtime);
+ kwlt.writeToParcel(out, uSecRealtime);
+ } else {
+ out.writeInt(0);
+ }
+ }
+ out.writeInt(mWakeupReasonStats.size());
+ for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
+ LongSamplingCounter counter = ent.getValue();
+ if (counter != null) {
+ out.writeInt(1);
+ out.writeString(ent.getKey());
+ counter.writeToParcel(out);
} else {
out.writeInt(0);
}
@@ -7383,6 +7573,8 @@
}
pr.println("*** Mobile network active timer:");
mMobileRadioActiveTimer.logState(pr, " ");
+ pr.println("*** Mobile network active adjusted timer:");
+ mMobileRadioActiveAdjustedTime.logState(pr, " ");
pr.println("*** Wifi timer:");
mWifiOnTimer.logState(pr, " ");
pr.println("*** WifiRunning timer:");
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/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/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/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9de3efe..41ffdc1 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -103,6 +103,7 @@
private long mNextNonWakeup;
int mBroadcastRefCount = 0;
PowerManager.WakeLock mWakeLock;
+ boolean mLastWakeLockUnimportantForLogging;
ArrayList<InFlight> mInFlight = new ArrayList<InFlight>();
final AlarmHandler mHandler = new AlarmHandler();
ClockReceiver mClockReceiver;
@@ -1345,17 +1346,21 @@
*/
void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, boolean first) {
try {
- mWakeLock.setUnimportantForLogging(pi == mTimeTickSender);
+ final boolean unimportant = pi == mTimeTickSender;
+ mWakeLock.setUnimportantForLogging(unimportant);
if (ws != null) {
- if (first) {
+ if (first || mLastWakeLockUnimportantForLogging) {
mWakeLock.setHistoryTag(pi.getTag(
type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
? "*walarm*:" : "*alarm*:"));
} else {
mWakeLock.setHistoryTag(null);
}
+ mLastWakeLockUnimportantForLogging = unimportant;
mWakeLock.setWorkSource(ws);
return;
+ } else {
+ mLastWakeLockUnimportantForLogging = false;
}
final int uid = ActivityManagerNative.getDefault()
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/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index a09d605..5273cec 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -348,7 +348,7 @@
*/
private void notifyInterfaceClassActivity(int type, boolean active, long tsNanos) {
try {
- getBatteryStats().noteDataConnectionActive(type, active);
+ getBatteryStats().noteDataConnectionActive(type, active, tsNanos);
} catch (RemoteException e) {
}
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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5500b9d..239e423 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6820,7 +6820,7 @@
}
}
- if (pending == null && receiver != null) {
+ if (pending.pendingRecords.isEmpty() && receiver != null) {
// In this case all thumbnails were available and the client
// is being asked to be told when the remaining ones come in...
// which is unusually, since the top-most currently running
@@ -6895,6 +6895,21 @@
rti.stackId = tr.stack.mStackId;
rti.userId = tr.userId;
+ final ArrayList<ActivityRecord> activities = tr.mActivities;
+ int numSet = 0;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0 && numSet < 2;
+ --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (rti.activityLabel == null && r.recentsLabel != null) {
+ rti.activityLabel = r.recentsLabel;
+ ++numSet;
+ }
+ if (rti.activityIcon == null && r.recentsIcon != null) {
+ rti.activityIcon = r.recentsIcon;
+ ++numSet;
+ }
+ }
+
if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) {
// Check whether this activity is currently available.
try {
@@ -6960,6 +6975,26 @@
}
@Override
+ public void setRecentsLabel(IBinder token, CharSequence recentsLabel) {
+ synchronized (this) {
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.recentsLabel = recentsLabel.toString();
+ }
+ }
+ }
+
+ @Override
+ public void setRecentsIcon(IBinder token, Bitmap recentsIcon) {
+ synchronized (this) {
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.recentsIcon = recentsIcon;
+ }
+ }
+ }
+
+ @Override
public boolean removeSubTask(int taskId, int subTaskIndex) {
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 5d23fc3..645a5e8 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -148,6 +148,9 @@
boolean mStartingWindowShown = false;
ActivityContainer mInitialActivityContainer;
+ String recentsLabel;
+ Bitmap recentsIcon;
+
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index fa4a9d1..0ddb827 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -248,10 +248,10 @@
}
}
- public void noteDataConnectionActive(int type, boolean active) {
+ public void noteDataConnectionActive(int type, boolean active, long timestampNs) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteDataConnectionActive(type, active);
+ mStats.noteDataConnectionActive(type, active, timestampNs);
}
}
@@ -570,9 +570,12 @@
int num;
while ((num=nativeWaitWakeup(mIrqs, mReasons)) >= 0) {
synchronized (mStats) {
- for (int i=0; i<num; i++) {
- //Slog.i(TAG, "Wakeup: irq #" + mIrqs[i] + " reason=" + mReasons[i]);
- mStats.noteWakeupReasonLocked(mIrqs[i], mReasons[i]);
+ if (num > 0) {
+ for (int i=0; i<num; i++) {
+ mStats.noteWakeupReasonLocked(mReasons[i]);
+ }
+ } else {
+ mStats.noteWakeupReasonLocked("unknown");
}
}
}
@@ -653,7 +656,7 @@
dumpHelp(pw);
return;
} else if ("-a".equals(arg)) {
- // fall through
+ flags |= BatteryStats.DUMP_VERBOSE;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
pw.println("Unknown option: " + arg);
dumpHelp(pw);
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/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cb4cf98..a01c586 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -200,6 +200,9 @@
private static final boolean GET_CERTIFICATES = true;
+ // Cap the size of permission trees that 3rd party apps can define
+ private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text
+
private static final int REMOVE_EVENTS =
FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_FROM;
private static final int ADD_EVENTS =
@@ -2357,7 +2360,35 @@
//if (pi1.descriptionRes != pi2.descriptionRes) return false;
return true;
}
-
+
+ int permissionInfoFootprint(PermissionInfo info) {
+ int size = info.name.length();
+ if (info.nonLocalizedLabel != null) size += info.nonLocalizedLabel.length();
+ if (info.nonLocalizedDescription != null) size += info.nonLocalizedDescription.length();
+ return size;
+ }
+
+ int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
+ int size = 0;
+ for (BasePermission perm : mSettings.mPermissions.values()) {
+ if (perm.uid == tree.uid) {
+ size += perm.name.length() + permissionInfoFootprint(perm.perm.info);
+ }
+ }
+ return size;
+ }
+
+ void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
+ // We calculate the max size of permissions defined by this uid and throw
+ // if that plus the size of 'info' would exceed our stated maximum.
+ if (tree.uid != Process.SYSTEM_UID) {
+ final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree);
+ if (curTreeSize + permissionInfoFootprint(info) > MAX_PERMISSION_TREE_FOOTPRINT) {
+ throw new SecurityException("Permission tree size cap exceeded");
+ }
+ }
+ }
+
boolean addPermissionLocked(PermissionInfo info, boolean async) {
if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
throw new SecurityException("Label must be specified in permission");
@@ -2368,6 +2399,7 @@
boolean changed = true;
int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
if (added) {
+ enforcePermissionCapLocked(info, tree);
bp = new BasePermission(info.name, tree.sourcePackage,
BasePermission.TYPE_DYNAMIC);
} else if (bp.type != BasePermission.TYPE_DYNAMIC) {
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/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_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 22cc519..da4cc48 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -139,16 +139,21 @@
}
endpos++;
}
- if (i == 0) {
+ // For now we are not separating out the first irq.
+ // This is because in practice there are always multiple
+ // lines of wakeup reasons, so it is better to just treat
+ // them all together as a single string.
+ if (false && i == 0) {
firstirq = irq;
} else {
- int len = snprintf(mergedreasonpos, remainreasonlen, ":%d", irq);
+ int len = snprintf(mergedreasonpos, remainreasonlen,
+ i == 0 ? "%d" : ":%d", irq);
if (len >= 0 && len < remainreasonlen) {
mergedreasonpos += len;
remainreasonlen -= len;
}
}
- int len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%s" : ":%s", pos);
+ int len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
if (len >= 0 && len < remainreasonlen) {
mergedreasonpos += len;
remainreasonlen -= len;
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/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);