Merge "Delete local references to avoid reference table overflow." into kraken
diff --git a/Android.mk b/Android.mk
index 5c95d20..54ce115 100644
--- a/Android.mk
+++ b/Android.mk
@@ -86,7 +86,6 @@
core/java/android/app/ISearchManager.aidl \
core/java/android/app/ISearchManagerCallback.aidl \
core/java/android/app/IServiceConnection.aidl \
- core/java/android/app/IStatusBar.aidl \
core/java/android/app/IThumbnailReceiver.aidl \
core/java/android/app/ITransientNotification.aidl \
core/java/android/app/IUiModeManager.aidl \
@@ -155,6 +154,8 @@
core/java/com/android/internal/backup/IBackupTransport.aidl \
core/java/com/android/internal/os/IDropBoxManagerService.aidl \
core/java/com/android/internal/os/IResultReceiver.aidl \
+ core/java/com/android/internal/statusbar/IStatusBar.aidl \
+ core/java/com/android/internal/statusbar/IStatusBarService.aidl \
core/java/com/android/internal/view/IInputContext.aidl \
core/java/com/android/internal/view/IInputContextCallback.aidl \
core/java/com/android/internal/view/IInputMethod.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 6455103..26d8a1b 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -53,6 +53,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/FrameworkTest_intermediates/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.policy*)
$(call add-clean-step, rm -rf $(TARGET_OUT_JAVA_LIBRARIES)/android.policy.jar)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
# ************************************************
diff --git a/core/java/android/app/IStatusBar.aidl b/core/java/android/app/IStatusBar.aidl
deleted file mode 100644
index c64fa50..0000000
--- a/core/java/android/app/IStatusBar.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Copyright (c) 2007, 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.app;
-
-/** @hide */
-interface IStatusBar
-{
- void activate();
- void deactivate();
- void toggle();
- void disable(int what, IBinder token, String pkg);
- IBinder addIcon(String slot, String iconPackage, int iconId, int iconLevel);
- void updateIcon(IBinder key, String slot, String iconPackage, int iconId, int iconLevel);
- void removeIcon(IBinder key);
-}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4d72f73..739aca3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -341,6 +341,44 @@
iconLevel = parcel.readInt();
}
+ public Notification clone() {
+ Notification that = new Notification();
+
+ that.when = this.when;
+ that.icon = this.icon;
+ that.number = this.number;
+
+ // PendingIntents are global, so there's no reason (or way) to clone them.
+ that.contentIntent = this.contentIntent;
+ that.deleteIntent = this.deleteIntent;
+
+ if (this.tickerText != null) {
+ that.tickerText = this.tickerText.toString();
+ }
+ if (this.contentView != null) {
+ that.contentView = this.contentView.clone();
+ }
+ that.iconLevel = that.iconLevel;
+ that.sound = this.sound; // android.net.Uri is immutable
+ that.audioStreamType = this.audioStreamType;
+
+ final long[] vibrate = this.vibrate;
+ if (vibrate != null) {
+ final int N = vibrate.length;
+ final long[] vib = that.vibrate = new long[N];
+ System.arraycopy(vibrate, 0, vib, 0, N);
+ }
+
+ that.ledARGB = this.ledARGB;
+ that.ledOnMS = this.ledOnMS;
+ that.ledOffMS = this.ledOffMS;
+ that.defaults = this.defaults;
+
+ that.flags = this.flags;
+
+ return that;
+ }
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 72ec616..de544fb 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -23,6 +23,8 @@
import android.os.IBinder;
import android.os.ServiceManager;
+import com.android.internal.statusbar.IStatusBarService;
+
/**
* Allows an app to control the status bar.
*
@@ -58,12 +60,12 @@
public static final int DISABLE_NONE = 0x00000000;
private Context mContext;
- private IStatusBar mService;
+ private IStatusBarService mService;
private IBinder mToken = new Binder();
StatusBarManager(Context context) {
mContext = context;
- mService = IStatusBar.Stub.asInterface(
+ mService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
@@ -85,7 +87,7 @@
*/
public void expand() {
try {
- mService.activate();
+ mService.expand();
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
@@ -97,46 +99,34 @@
*/
public void collapse() {
try {
- mService.deactivate();
- } catch (RemoteException ex) {
- // system process is dead anyway.
- throw new RuntimeException(ex);
- }
- }
-
- /**
- * Toggle the status bar.
- */
- public void toggle() {
- try {
- mService.toggle();
+ mService.collapse();
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
- public IBinder addIcon(String slot, int iconId, int iconLevel) {
+ public void setIcon(String slot, int iconId, int iconLevel) {
try {
- return mService.addIcon(slot, mContext.getPackageName(), iconId, iconLevel);
+ mService.setIcon(slot, mContext.getPackageName(), iconId, iconLevel);
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
- public void updateIcon(IBinder key, String slot, int iconId, int iconLevel) {
+ public void removeIcon(String slot) {
try {
- mService.updateIcon(key, slot, mContext.getPackageName(), iconId, iconLevel);
+ mService.removeIcon(slot);
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
- public void removeIcon(IBinder key) {
+ public void setIconVisibility(String slot, boolean visible) {
try {
- mService.removeIcon(key);
+ mService.setIconVisibility(slot, visible);
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index d4f9b20..0e798dc 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -33,6 +33,8 @@
import android.util.Log;
import android.util.Pair;
+import dalvik.system.BlockGuard;
+
import java.io.File;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
@@ -1339,6 +1341,7 @@
if (!isOpen()) {
throw new IllegalStateException("database not open");
}
+ BlockGuard.getThreadPolicy().onReadFromDisk();
long timeStart = 0;
if (Config.LOGV || mSlowQueryThreshold != -1) {
@@ -1497,6 +1500,7 @@
*/
public long insertWithOnConflict(String table, String nullColumnHack,
ContentValues initialValues, int conflictAlgorithm) {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
if (!isOpen()) {
throw new IllegalStateException("database not open");
}
@@ -1588,6 +1592,7 @@
* whereClause.
*/
public int delete(String table, String whereClause, String[] whereArgs) {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
lock();
if (!isOpen()) {
throw new IllegalStateException("database not open");
@@ -1643,6 +1648,7 @@
*/
public int updateWithOnConflict(String table, ContentValues values,
String whereClause, String[] whereArgs, int conflictAlgorithm) {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
if (values == null || values.size() == 0) {
throw new IllegalArgumentException("Empty values");
}
@@ -1725,6 +1731,7 @@
* @throws SQLException If the SQL string is invalid for some reason
*/
public void execSQL(String sql) throws SQLException {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
long timeStart = SystemClock.uptimeMillis();
lock();
if (!isOpen()) {
@@ -1760,6 +1767,7 @@
* @throws SQLException If the SQL string is invalid for some reason
*/
public void execSQL(String sql, Object[] bindArgs) throws SQLException {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
if (bindArgs == null) {
throw new IllegalArgumentException("Empty bindArgs");
}
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 47cca87..9e425c3 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -18,6 +18,8 @@
import android.os.SystemClock;
+import dalvik.system.BlockGuard;
+
/**
* A pre-compiled statement against a {@link SQLiteDatabase} that can be reused.
* The statement cannot return multiple rows, but 1x1 result sets are allowed.
@@ -47,6 +49,7 @@
* some reason
*/
public void execute() {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
@@ -73,6 +76,7 @@
* some reason
*/
public long executeInsert() {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
@@ -99,6 +103,7 @@
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
*/
public long simpleQueryForLong() {
+ BlockGuard.getThreadPolicy().onReadFromDisk();
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
@@ -125,6 +130,7 @@
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
*/
public String simpleQueryForString() {
+ BlockGuard.getThreadPolicy().onReadFromDisk();
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 54aa363..025db4a 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -763,6 +763,7 @@
private static final String KEY_ZOOM_SUPPORTED = "zoom-supported";
private static final String KEY_SMOOTH_ZOOM_SUPPORTED = "smooth-zoom-supported";
private static final String KEY_FOCUS_DISTANCES = "focus-distances";
+ private static final String KEY_METERING_MODE = "metering-mode";
// Parameter key suffix for supported values.
private static final String SUPPORTED_VALUES_SUFFIX = "-values";
@@ -965,6 +966,26 @@
*/
public static final String FOCUS_MODE_CONTINUOUS = "continuous";
+ /**
+ * The camera determines the exposure by giving more weight to the
+ * central part of the scene.
+ * @hide
+ */
+ public static final String METERING_MODE_CENTER_WEIGHTED = "center-weighted";
+
+ /**
+ * The camera determines the exposure by averaging the entire scene,
+ * giving no weighting to any particular area.
+ * @hide
+ */
+ public static final String METERING_MODE_FRAME_AVERAGE = "frame-average";
+
+ /**
+ * The camera determines the exposure by a very small area of the scene,
+ * typically the center.
+ * @hide
+ */
+ public static final String METERING_MODE_SPOT = "spot";
// Formats for setPreviewFormat and setPictureFormat.
private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp";
@@ -1930,6 +1951,45 @@
output[2] = distances.get(2);
}
+ /**
+ * Gets the supported metering modes.
+ *
+ * @return a list of supported metering modes. null if metering mode
+ * setting is not supported.
+ * @see #getMeteringMode()
+ * @hide
+ */
+ public List<String> getSupportedMeteringModes() {
+ String str = get(KEY_METERING_MODE + SUPPORTED_VALUES_SUFFIX);
+ return split(str);
+ }
+
+ /**
+ * Gets the current metering mode, which affects how camera determines
+ * exposure.
+ *
+ * @return current metering mode. If the camera does not support
+ * metering setting, this should return null.
+ * @see #METERING_MODE_CENTER_WEIGHTED
+ * @see #METERING_MODE_FRAME_AVERAGE
+ * @see #METERING_MODE_SPOT
+ * @hide
+ */
+ public String getMeteringMode() {
+ return get(KEY_METERING_MODE);
+ }
+
+ /**
+ * Sets the metering mode.
+ *
+ * @param value metering mode.
+ * @see #getMeteringMode()
+ * @hide
+ */
+ public void setMeteringMode(String value) {
+ set(KEY_METERING_MODE, value);
+ }
+
// Splits a comma delimited string to an ArrayList of String.
// Return null if the passing string is null or the size is 0.
private ArrayList<String> split(String str) {
diff --git a/core/java/android/text/util/Rfc822Tokenizer.java b/core/java/android/text/util/Rfc822Tokenizer.java
index 952d833..69cf93c 100644
--- a/core/java/android/text/util/Rfc822Tokenizer.java
+++ b/core/java/android/text/util/Rfc822Tokenizer.java
@@ -84,8 +84,10 @@
if (c == '"') {
i++;
break;
- } else if (c == '\\' && i + 1 < cursor) {
- name.append(text.charAt(i + 1));
+ } else if (c == '\\') {
+ if (i + 1 < cursor) {
+ name.append(text.charAt(i + 1));
+ }
i += 2;
} else {
name.append(c);
@@ -110,8 +112,10 @@
comment.append(c);
level++;
i++;
- } else if (c == '\\' && i + 1 < cursor) {
- comment.append(text.charAt(i + 1));
+ } else if (c == '\\') {
+ if (i + 1 < cursor) {
+ comment.append(text.charAt(i + 1));
+ }
i += 2;
} else {
comment.append(c);
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index d648e96..eefbf7a 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -255,17 +255,19 @@
}
static private MotionEvent obtain() {
+ final MotionEvent ev;
synchronized (gRecyclerLock) {
if (gRecyclerTop == null) {
return new MotionEvent();
}
- MotionEvent ev = gRecyclerTop;
+ ev = gRecyclerTop;
gRecyclerTop = ev.mNext;
gRecyclerUsed--;
- ev.mRecycledLocation = null;
- ev.mRecycled = false;
- return ev;
}
+ ev.mRecycledLocation = null;
+ ev.mRecycled = false;
+ ev.mNext = null;
+ return ev;
}
/**
@@ -620,11 +622,14 @@
throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
}
mRecycledLocation = new RuntimeException("Last recycled here");
- } else if (mRecycled) {
- throw new RuntimeException(toString() + " recycled twice!");
+ //Log.w("MotionEvent", "Recycling event " + this, mRecycledLocation);
+ } else {
+ if (mRecycled) {
+ throw new RuntimeException(toString() + " recycled twice!");
+ }
+ mRecycled = true;
}
- //Log.w("MotionEvent", "Recycling event " + this, mRecycledLocation);
synchronized (gRecyclerLock) {
if (gRecyclerUsed < MAX_RECYCLED) {
gRecyclerUsed++;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 3003580..7a70c80 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -100,6 +100,7 @@
* Base class for all actions that can be performed on an
* inflated view.
*
+ * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
*/
private abstract static class Action implements Parcelable {
public abstract void apply(View root) throws ActionException;
@@ -568,6 +569,14 @@
}
}
+ public RemoteViews clone() {
+ final RemoteViews that = new RemoteViews(mPackage, mLayoutId);
+ if (mActions != null) {
+ that.mActions = (ArrayList<Action>)mActions.clone();
+ }
+ return that;
+ }
+
public String getPackage() {
return mPackage;
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
new file mode 100644
index 0000000..4501bd7
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2007, 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.internal.statusbar;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarNotification;
+
+/** @hide */
+oneway interface IStatusBar
+{
+ void setIcon(int index, in StatusBarIcon icon);
+ void removeIcon(int index);
+ void addNotification(IBinder key, in StatusBarNotification notification);
+ void updateNotification(IBinder key, in StatusBarNotification notification);
+ void removeNotification(IBinder key);
+ void disable(int state);
+ void animateExpand();
+ void animateCollapse();
+}
+
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
new file mode 100644
index 0000000..1f25b37
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2007, 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.internal.statusbar;
+
+import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIconList;
+import com.android.internal.statusbar.StatusBarNotification;
+
+/** @hide */
+interface IStatusBarService
+{
+ void expand();
+ void collapse();
+ void disable(int what, IBinder token, String pkg);
+ void setIcon(String slot, String iconPackage, int iconId, int iconLevel);
+ void setIconVisibility(String slot, boolean visible);
+ void removeIcon(String slot);
+
+ // ---- Methods below are for use by the status bar policy services ----
+ void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList,
+ out List<IBinder> notificationKeys, out List<StatusBarNotification> notifications);
+ void visibilityChanged(boolean visible);
+ void onNotificationClick(String pkg, String tag, int id);
+ void onClearAllNotifications();
+}
diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.aidl b/core/java/com/android/internal/statusbar/StatusBarIcon.aidl
new file mode 100644
index 0000000..311a077
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/StatusBarIcon.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2010, 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.internal.statusbar;
+
+parcelable StatusBarIcon;
+
diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java
new file mode 100644
index 0000000..ae2cac2
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 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.internal.statusbar;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public class StatusBarIcon implements Parcelable {
+ public String iconPackage;
+ public int iconId;
+ public int iconLevel;
+ public boolean visible = true;
+ public int number;
+
+ private StatusBarIcon() {
+ }
+
+ public StatusBarIcon(String iconPackage, int iconId, int iconLevel) {
+ this.iconPackage = iconPackage;
+ this.iconId = iconId;
+ this.iconLevel = iconLevel;
+ }
+
+ public StatusBarIcon(String iconPackage, int iconId, int iconLevel, int number) {
+ this.iconPackage = iconPackage;
+ this.iconId = iconId;
+ this.iconLevel = iconLevel;
+ this.number = number;
+ }
+
+ public String toString() {
+ return "StatusBarIcon(pkg=" + this.iconPackage + " id=0x" + Integer.toHexString(this.iconId)
+ + " level=" + this.iconLevel + " visible=" + visible
+ + " num=" + this.number + " )";
+ }
+
+ public StatusBarIcon clone() {
+ StatusBarIcon that = new StatusBarIcon(this.iconPackage, this.iconId, this.iconLevel);
+ that.visible = this.visible;
+ that.number = this.number;
+ return that;
+ }
+
+ /**
+ * Unflatten the StatusBarIcon from a parcel.
+ */
+ public StatusBarIcon(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public void readFromParcel(Parcel in) {
+ this.iconPackage = in.readString();
+ this.iconId = in.readInt();
+ this.iconLevel = in.readInt();
+ this.visible = in.readInt() != 0;
+ this.number = in.readInt();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(this.iconPackage);
+ out.writeInt(this.iconId);
+ out.writeInt(this.iconLevel);
+ out.writeInt(this.visible ? 1 : 0);
+ out.writeInt(this.number);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Parcelable.Creator that instantiates StatusBarIcon objects
+ */
+ public static final Parcelable.Creator<StatusBarIcon> CREATOR
+ = new Parcelable.Creator<StatusBarIcon>()
+ {
+ public StatusBarIcon createFromParcel(Parcel parcel)
+ {
+ return new StatusBarIcon(parcel);
+ }
+
+ public StatusBarIcon[] newArray(int size)
+ {
+ return new StatusBarIcon[size];
+ }
+ };
+}
+
diff --git a/core/java/com/android/internal/statusbar/StatusBarIconList.aidl b/core/java/com/android/internal/statusbar/StatusBarIconList.aidl
new file mode 100644
index 0000000..c745120
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/StatusBarIconList.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2010, 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.internal.statusbar;
+
+parcelable StatusBarIconList;
+
diff --git a/core/java/com/android/internal/statusbar/StatusBarIconList.java b/core/java/com/android/internal/statusbar/StatusBarIconList.java
new file mode 100644
index 0000000..478d245
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/StatusBarIconList.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2007 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.internal.statusbar;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.PrintWriter;
+
+public class StatusBarIconList implements Parcelable {
+ private String[] mSlots;
+ private StatusBarIcon[] mIcons;
+
+ public StatusBarIconList() {
+ }
+
+ public StatusBarIconList(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public void readFromParcel(Parcel in) {
+ this.mSlots = in.readStringArray();
+ final int N = in.readInt();
+ if (N < 0) {
+ mIcons = null;
+ } else {
+ mIcons = new StatusBarIcon[N];
+ for (int i=0; i<N; i++) {
+ if (in.readInt() != 0) {
+ mIcons[i] = new StatusBarIcon(in);
+ }
+ }
+ }
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeStringArray(mSlots);
+ if (mIcons == null) {
+ out.writeInt(-1);
+ } else {
+ final int N = mIcons.length;
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ StatusBarIcon ic = mIcons[i];
+ if (ic == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(1);
+ ic.writeToParcel(out, flags);
+ }
+ }
+ }
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Parcelable.Creator that instantiates StatusBarIconList objects
+ */
+ public static final Parcelable.Creator<StatusBarIconList> CREATOR
+ = new Parcelable.Creator<StatusBarIconList>()
+ {
+ public StatusBarIconList createFromParcel(Parcel parcel)
+ {
+ return new StatusBarIconList(parcel);
+ }
+
+ public StatusBarIconList[] newArray(int size)
+ {
+ return new StatusBarIconList[size];
+ }
+ };
+
+ public void defineSlots(String[] slots) {
+ final int N = slots.length;
+ String[] s = mSlots = new String[N];
+ for (int i=0; i<N; i++) {
+ s[i] = slots[i];
+ }
+ mIcons = new StatusBarIcon[N];
+ }
+
+ public int getSlotIndex(String slot) {
+ final int N = mSlots.length;
+ for (int i=0; i<N; i++) {
+ if (slot.equals(mSlots[i])) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public int size() {
+ return mSlots.length;
+ }
+
+ public void setIcon(int index, StatusBarIcon icon) {
+ mIcons[index] = icon.clone();
+ }
+
+ public void removeIcon(int index) {
+ mIcons[index] = null;
+ }
+
+ public String getSlot(int index) {
+ return mSlots[index];
+ }
+
+ public StatusBarIcon getIcon(int index) {
+ return mIcons[index];
+ }
+
+ public int getViewIndex(int index) {
+ int count = 0;
+ for (int i=0; i<index; i++) {
+ if (mIcons[i] != null) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public void copyFrom(StatusBarIconList that) {
+ if (that.mSlots == null) {
+ this.mSlots = null;
+ this.mIcons = null;
+ } else {
+ final int N = that.mSlots.length;
+ this.mSlots = new String[N];
+ this.mIcons = new StatusBarIcon[N];
+ for (int i=0; i<N; i++) {
+ this.mSlots[i] = that.mSlots[i];
+ this.mIcons[i] = that.mIcons[i] != null ? that.mIcons[i].clone() : null;
+ }
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ final int N = mSlots.length;
+ pw.println("Icon list:");
+ for (int i=0; i<N; i++) {
+ pw.printf(" %2d: (%s) %s\n", i, mSlots[i], mIcons[i]);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.aidl b/core/java/com/android/internal/statusbar/StatusBarNotification.aidl
new file mode 100644
index 0000000..bd9e89c
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/StatusBarNotification.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2010, 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.internal.statusbar;
+
+parcelable StatusBarNotification;
+
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/com/android/internal/statusbar/StatusBarNotification.java
new file mode 100644
index 0000000..5499676
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/StatusBarNotification.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.statusbar;
+
+import android.app.Notification;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.widget.RemoteViews;
+
+
+/*
+boolean clearable = !n.ongoingEvent && ((notification.flags & Notification.FLAG_NO_CLEAR) == 0);
+
+
+// TODO: make this restriction do something smarter like never fill
+// more than two screens. "Why would anyone need more than 80 characters." :-/
+final int maxTickerLen = 80;
+if (truncatedTicker != null && truncatedTicker.length() > maxTickerLen) {
+ truncatedTicker = truncatedTicker.subSequence(0, maxTickerLen);
+}
+*/
+
+public class StatusBarNotification implements Parcelable {
+ public String pkg;
+ public int id;
+ public String tag;
+ public Notification notification;
+
+ public StatusBarNotification() {
+ }
+
+ public StatusBarNotification(String pkg, int id, String tag, Notification notification) {
+ if (pkg == null) throw new NullPointerException();
+ if (notification == null) throw new NullPointerException();
+
+ this.pkg = pkg;
+ this.id = id;
+ this.tag = tag;
+ this.notification = notification;
+ }
+
+ public StatusBarNotification(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public void readFromParcel(Parcel in) {
+ this.pkg = in.readString();
+ this.id = in.readInt();
+ if (in.readInt() != 0) {
+ this.tag = in.readString();
+ } else {
+ this.tag = null;
+ }
+ this.notification = new Notification(in);
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(this.pkg);
+ out.writeInt(this.id);
+ if (this.tag != null) {
+ out.writeInt(1);
+ out.writeString(this.tag);
+ } else {
+ out.writeInt(0);
+ }
+ this.notification.writeToParcel(out, flags);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<StatusBarNotification> CREATOR
+ = new Parcelable.Creator<StatusBarNotification>()
+ {
+ public StatusBarNotification createFromParcel(Parcel parcel)
+ {
+ return new StatusBarNotification(parcel);
+ }
+
+ public StatusBarNotification[] newArray(int size)
+ {
+ return new StatusBarNotification[size];
+ }
+ };
+
+ public StatusBarNotification clone() {
+ return new StatusBarNotification(this.pkg, this.id, this.tag, this.notification.clone());
+ }
+
+ public String toString() {
+ return "StatusBarNotification(package=" + pkg + " id=" + id + " tag=" + tag
+ + " notification=" + notification + ")";
+ }
+
+ public boolean isOngoing() {
+ return (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
+ }
+
+}
+
+
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotificationList.aidl b/core/java/com/android/internal/statusbar/StatusBarNotificationList.aidl
new file mode 100644
index 0000000..10abeee
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/StatusBarNotificationList.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2010, 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.internal.statusbar;
+
+parcelable StatusBarNotificationList;
+
diff --git a/core/res/res/drawable-hdpi/battery_charge_fill_empty.9.png b/core/res/res/drawable-hdpi/battery_charge_fill_empty.9.png
deleted file mode 100644
index c4e70a8..0000000
--- a/core/res/res/drawable-hdpi/battery_charge_fill_empty.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/battery_charge_fill_full.9.png b/core/res/res/drawable-hdpi/battery_charge_fill_full.9.png
deleted file mode 100644
index ac66f5a..0000000
--- a/core/res/res/drawable-hdpi/battery_charge_fill_full.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/battery_charge_fill_warning.9.png b/core/res/res/drawable-hdpi/battery_charge_fill_warning.9.png
deleted file mode 100644
index 32d99c6..0000000
--- a/core/res/res/drawable-hdpi/battery_charge_fill_warning.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png
index 96dc085..7a8b78f 100644
--- a/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png
+++ b/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/battery_charge_fill_full.9.png b/core/res/res/drawable-mdpi/battery_charge_fill_full.9.png
deleted file mode 100644
index 8e6aaca..0000000
--- a/core/res/res/drawable-mdpi/battery_charge_fill_full.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/battery_charge_fill_warning.9.png b/core/res/res/drawable-mdpi/battery_charge_fill_warning.9.png
deleted file mode 100644
index d3287db..0000000
--- a/core/res/res/drawable-mdpi/battery_charge_fill_warning.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/battery_charge_fill.xml b/core/res/res/drawable/battery_charge_fill.xml
deleted file mode 100644
index 7f65733..0000000
--- a/core/res/res/drawable/battery_charge_fill.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<level-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:maxLevel="29" android:drawable="@android:drawable/battery_charge_fill_empty" />
- <item android:maxLevel="49" android:drawable="@android:drawable/battery_charge_fill_warning" />
- <item android:maxLevel="100" android:drawable="@android:drawable/battery_charge_fill_full" />
-</level-list>
-
diff --git a/core/res/res/layout/battery_status.xml b/core/res/res/layout/battery_status.xml
deleted file mode 100644
index 7cfec05..0000000
--- a/core/res/res/layout/battery_status.xml
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/frame"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center_horizontal"
- >
-
- <FrameLayout
- android:layout_width="141px"
- android:layout_height="184px"
- android:background="@drawable/battery_charge_background"
- android:paddingTop="25px"
- android:paddingLeft="1px"
- >
-
- <LinearLayout
- android:id="@+id/meter"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- >
-
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="15dip"
- />
- <ImageView
- android:id="@+id/spacer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- <ImageView
- android:id="@+id/level"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
-
- </LinearLayout>
-
- <TextView android:id="@+id/level_percent"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:textStyle="bold"
- android:textSize="48dp"
- android:textColor="#ffffffff"
- android:gravity="center"
- />
- </FrameLayout>
-
- <TextView android:id="@+id/status"
- android:paddingTop="35dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textStyle="bold"
- android:textSize="30dp"
- android:textColor="#ffffffff"
- android:gravity="center_horizontal"
- android:text="@string/battery_status_charging"
- />
-
-</LinearLayout>
-
-
diff --git a/core/res/res/layout/status_bar_latest_event_content.xml b/core/res/res/layout/status_bar_latest_event_content.xml
index ea6200a..c64b90e 100644
--- a/core/res/res/layout/status_bar_latest_event_content.xml
+++ b/core/res/res/layout/status_bar_latest_event_content.xml
@@ -12,7 +12,8 @@
android:orientation="horizontal"
android:paddingTop="3dp"
>
- <com.android.server.status.AnimatedImageView android:id="@+id/icon"
+ <!--com.android.server.status.AnimatedImageView android:id="@+id/icon" -->
+ <ImageView android:id="@+id/icon"
android:layout_width="25dp"
android:layout_height="25dp"
android:scaleType="fitCenter"
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index a0bc5b3..0493756 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -120,25 +120,25 @@
<!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the
icons in the status bar that are not notifications. -->
<string-array name="status_bar_icon_order">
- <item><xliff:g id="id">clock</xliff:g></item>
- <item><xliff:g id="id">secure</xliff:g></item>
- <item><xliff:g id="id">alarm_clock</xliff:g></item>
- <item><xliff:g id="id">battery</xliff:g></item>
- <item><xliff:g id="id">phone_signal</xliff:g></item>
- <item><xliff:g id="id">phone_evdo_signal</xliff:g></item>
- <item><xliff:g id="id">data_connection</xliff:g></item>
- <item><xliff:g id="id">cdma_eri</xliff:g></item>
- <item><xliff:g id="id">wifi</xliff:g></item>
- <item><xliff:g id="id">tty</xliff:g></item>
- <item><xliff:g id="id">volume</xliff:g></item>
- <item><xliff:g id="id">mute</xliff:g></item>
- <item><xliff:g id="id">speakerphone</xliff:g></item>
- <item><xliff:g id="id">tty</xliff:g></item>
- <item><xliff:g id="id">bluetooth</xliff:g></item>
- <item><xliff:g id="id">gps</xliff:g></item>
- <item><xliff:g id="id">sync_active</xliff:g></item>
- <item><xliff:g id="id">sync_failing</xliff:g></item>
- <item><xliff:g id="id">ime</xliff:g></item>
+ <item><xliff:g id="id">ime</xliff:g></item>
+ <item><xliff:g id="id">sync_failing</xliff:g></item>
+ <item><xliff:g id="id">sync_active</xliff:g></item>
+ <item><xliff:g id="id">gps</xliff:g></item>
+ <item><xliff:g id="id">bluetooth</xliff:g></item>
+ <item><xliff:g id="id">tty</xliff:g></item>
+ <item><xliff:g id="id">speakerphone</xliff:g></item>
+ <item><xliff:g id="id">mute</xliff:g></item>
+ <item><xliff:g id="id">volume</xliff:g></item>
+ <item><xliff:g id="id">tty</xliff:g></item>
+ <item><xliff:g id="id">wifi</xliff:g></item>
+ <item><xliff:g id="id">cdma_eri</xliff:g></item>
+ <item><xliff:g id="id">data_connection</xliff:g></item>
+ <item><xliff:g id="id">phone_evdo_signal</xliff:g></item>
+ <item><xliff:g id="id">phone_signal</xliff:g></item>
+ <item><xliff:g id="id">battery</xliff:g></item>
+ <item><xliff:g id="id">alarm_clock</xliff:g></item>
+ <item><xliff:g id="id">secure</xliff:g></item>
+ <item><xliff:g id="id">clock</xliff:g></item>
</string-array>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 64f05fe..cd47ae9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -220,9 +220,6 @@
<!-- Allow the menu hard key to be disabled in LockScreen on some devices -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
- <!-- Control whether status bar should distinguish HSPA data icon form UMTS data icon on devices -->
- <bool name="config_hspa_data_distinguishable">false</bool>
-
<!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
The N entries of this array define N + 1 zones as follows:
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1e007d36..613a9a2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1567,45 +1567,6 @@
<!-- A format string for 12-hour time of day, just the hour, not the minute, with capital "AM" or "PM" (example: "3PM"). -->
<string name="hour_cap_ampm">"<xliff:g id="hour" example="3">%-l</xliff:g><xliff:g id="ampm" example="PM">%p</xliff:g>"</string>
- <!-- The text for the button in the notification window-shade that clears
- all of the currently visible notifications. -->
- <string name="status_bar_clear_all_button">Clear</string>
-
- <!-- The label in the bar at the top of the status bar when there are no notifications
- showing. -->
- <string name="status_bar_no_notifications_title">No notifications</string>
-
- <!-- The label for the group of notifications for ongoing events in the opened version of
- the status bar. An ongoing call is the prime example of this. The MP3 music player
- might be another example. -->
- <string name="status_bar_ongoing_events_title">Ongoing</string>
-
- <!-- The label for the group of notifications for recent events in the opened version of
- the status bar. Recently received text messsages (SMS), emails, calendar alerts, etc. -->
- <string name="status_bar_latest_events_title">Notifications</string>
-
- <!-- The big percent text in the middle of the battery icon that appears when you plug in
- the charger. -->
- <string name="battery_status_text_percent_format"><xliff:g id="number" example="50">%d</xliff:g><xliff:g id="percent" example="%">%%</xliff:g></string>
-
- <!-- The big percent text in the middle of the battery icon that appears when you plug in
- the charger. This indicates the current status of the battery. -->
- <string name="battery_status_charging">Charging\u2026</string>
-
- <!-- When the battery is low, this is displayed to the user in a dialog. The title of the low battery alert. -->
- <string name="battery_low_title">Please connect charger</string>
-
- <!-- When the battery is low, this is displayed to the user in a dialog. The subtitle of the low battery alert. -->
- <string name="battery_low_subtitle">The battery is getting low:</string>
-
- <!-- A message that appears when the battery level is getting low in a dialog. This is appened to the subtitle of the low battery alert. -->
- <string name="battery_low_percent_format"><xliff:g id="number">%d%%</xliff:g>
- or less remaining.</string>
-
- <!-- When the battery is low, this is the label of the button to go to the
- power usage activity to find out what drained the battery. -->
- <string name="battery_low_why">Battery use</string>
-
<!-- Title of the alert when something went wrong in the factory test. -->
<string name="factorytest_failed">Factory test failed</string>
<!-- Error message displayed when a non-system application tries to start a factory test. -->
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 5b427be..a5229cc 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -26,6 +26,8 @@
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.StyleSpan;
+import android.text.util.Rfc822Token;
+import android.text.util.Rfc822Tokenizer;
import android.test.MoreAsserts;
import com.android.common.Rfc822Validator;
@@ -269,6 +271,24 @@
}
}
+ @SmallTest
+ public void testRfc822TokenizerFullAddress() {
+ Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("Foo Bar (something) <foo@google.com>");
+ assertNotNull(tokens);
+ assertEquals(1, tokens.length);
+ assertEquals("foo@google.com", tokens[0].getAddress());
+ assertEquals("Foo Bar", tokens[0].getName());
+ assertEquals("something",tokens[0].getComment());
+ }
+
+ @SmallTest
+ public void testRfc822TokenizeItemWithError() {
+ Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("\"Foo Bar\\");
+ assertNotNull(tokens);
+ assertEquals(1, tokens.length);
+ assertEquals("Foo Bar", tokens[0].getAddress());
+ }
+
@LargeTest
public void testEllipsize() {
CharSequence s1 = "The quick brown fox jumps over \u00FEhe lazy dog.";
diff --git a/docs/html/guide/topics/security/security.jd b/docs/html/guide/topics/security/security.jd
index da201c4..dbc9866 100644
--- a/docs/html/guide/topics/security/security.jd
+++ b/docs/html/guide/topics/security/security.jd
@@ -40,15 +40,14 @@
e-mails), reading or writing another application's files, performing
network access, keeping the device awake, etc.<p>
-<p>An application's process is a secure sandbox. It can't disrupt other
-applications, except by explicitly declaring the <em>permissions</em> it needs
-for additional capabilities not provided by the basic sandbox. These
-permissions it requests can be handled by the operating in various ways,
-typically by automatically allowing or disallowing based on certificates or
-by prompting the user. The permissions required by an application are declared
-statically in that application, so they can be known up-front at install time
-and will not change after that.</p>
-
+<p>An application's process runs in a security sandbox. The sandbox is designed
+to prevent applications from disrupting each other, except by explicitly
+declaring the <em>permissions</em> they need for additional capabilities not
+provided by the basic sandbox. The system handles requests for permissions
+in various ways, typically by automatically allowing or disallowing based on
+certificates or by prompting the user. The permissions required by an
+application are declared statically in that application, so they can be known
+up-front at install time and will not change after that.</p>
<a name="signing"></a>
<h2>Application Signing</h2>
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 2b7a86eb..e12a694 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -250,6 +250,13 @@
// Example value: "yuv420sp" or PIXEL_FORMAT_XXX constants. Read only.
static const char KEY_VIDEO_FRAME_FORMAT[];
+ // Metering mode. This affects how camera determines exposure.
+ // Example value: "spot" or METERING_MODE_XXX constants. Read/write.
+ static const char KEY_METERING_MODE[];
+ // Supported metering modes.
+ // Example value: "center-weighted,frame-average,spot". Read only.
+ static const char KEY_SUPPORTED_METERING_MODES[];
+
// Value for KEY_ZOOM_SUPPORTED or KEY_SMOOTH_ZOOM_SUPPORTED.
static const char TRUE[];
@@ -347,6 +354,15 @@
// callback will be only called once as soon as the picture is in focus.
static const char FOCUS_MODE_CONTINUOUS[];
+ // The camera determines the exposure by giving more weight to the
+ // central part of the scene.
+ static const char METERING_MODE_CENTER_WEIGHTED[];
+ // The camera determines the exposure by averaging the entire scene,
+ // giving no weighting to any particular area.
+ static const char METERING_MODE_FRAME_AVERAGE[];
+ // The camera determines the exposure by a very small area of the scene,
+ // typically the center.
+ static const char METERING_MODE_SPOT[];
private:
DefaultKeyedVector<String8,String8> mMap;
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 92bc126..d956882 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -142,7 +142,8 @@
uint32_t flags = 0,
callback_t cbf = 0,
void* user = 0,
- int notificationFrames = 0);
+ int notificationFrames = 0,
+ int sessionId = 0);
/* Terminates the AudioRecord and unregisters it from AudioFlinger.
@@ -168,7 +169,8 @@
callback_t cbf = 0,
void* user = 0,
int notificationFrames = 0,
- bool threadCanCallJava = false);
+ bool threadCanCallJava = false,
+ int sessionId = 0);
/* Result of constructing the AudioRecord. This must be checked
@@ -270,6 +272,16 @@
*/
audio_io_handle_t getInput();
+ /* returns the audio session ID associated to this AudioRecord.
+ *
+ * Parameters:
+ * none.
+ *
+ * Returned value:
+ * AudioRecord session ID.
+ */
+ int getSessionId();
+
/* obtains a buffer of "frameCount" frames. The buffer must be
* filled entirely. If the track is stopped, obtainBuffer() returns
* STOPPED instead of NO_ERROR as long as there are buffers availlable,
@@ -356,6 +368,7 @@
uint32_t mFlags;
uint32_t mChannels;
audio_io_handle_t mInput;
+ int mSessionId;
};
}; // namespace android
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 9caef8f..f21e83d 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -230,6 +230,8 @@
static status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int stream = DEFAULT);
static unsigned int getInputFramesLost(audio_io_handle_t ioHandle);
+
+ static int newAudioSessionId();
//
// AudioPolicyService interface
//
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index cc4ab74..c46df1e 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -138,7 +138,8 @@
uint32_t flags = 0,
callback_t cbf = 0,
void* user = 0,
- int notificationFrames = 0);
+ int notificationFrames = 0,
+ int sessionId = 0);
/* Creates an audio track and registers it with AudioFlinger. With this constructor,
* The PCM data to be rendered by AudioTrack is passed in a shared memory buffer
@@ -157,7 +158,8 @@
uint32_t flags = 0,
callback_t cbf = 0,
void* user = 0,
- int notificationFrames = 0);
+ int notificationFrames = 0,
+ int sessionId = 0);
/* Terminates the AudioTrack and unregisters it from AudioFlinger.
* Also destroys all resources assotiated with the AudioTrack.
@@ -182,7 +184,8 @@
void* user = 0,
int notificationFrames = 0,
const sp<IMemory>& sharedBuffer = 0,
- bool threadCanCallJava = false);
+ bool threadCanCallJava = false,
+ int sessionId = 0);
/* Result of constructing the AudioTrack. This must be checked
@@ -239,10 +242,17 @@
/* set volume for this track, mostly used for games' sound effects
+ * left and right volumes. Levels must be <= 1.0.
*/
- void setVolume(float left, float right);
+ status_t setVolume(float left, float right);
void getVolume(float* left, float* right);
+ /* set the send level for this track. An auxiliary effect should be attached
+ * to the track with attachEffect(). Level must be <= 1.0.
+ */
+ status_t setSendLevel(float level);
+ void getSendLevel(float* level);
+
/* set sample rate for this track, mostly used for games' sound effects
*/
status_t setSampleRate(int sampleRate);
@@ -340,6 +350,31 @@
*/
audio_io_handle_t getOutput();
+ /* returns the unique ID associated to this track.
+ *
+ * Parameters:
+ * none.
+ *
+ * Returned value:
+ * AudioTrack ID.
+ */
+ int getSessionId();
+
+
+ /* Attach track auxiliary output to specified effect. Used effectId = 0
+ * to detach track from effect.
+ *
+ * Parameters:
+ *
+ * effectId: effectId obtained from AudioEffect::id().
+ *
+ * Returned status (from utils/Errors.h) can be:
+ * - NO_ERROR: successful operation
+ * - INVALID_OPERATION: the effect is not an auxiliary effect.
+ * - BAD_VALUE: The specified effect ID is invalid
+ */
+ status_t attachAuxEffect(int effectId);
+
/* obtains a buffer of "frameCount" frames. The buffer must be
* filled entirely. If the track is stopped, obtainBuffer() returns
* STOPPED instead of NO_ERROR as long as there are buffers availlable,
@@ -406,6 +441,7 @@
sp<AudioTrackThread> mAudioTrackThread;
float mVolume[2];
+ float mSendLevel;
uint32_t mFrameCount;
audio_track_cblk_t* mCblk;
@@ -431,6 +467,7 @@
uint32_t mNewPosition;
uint32_t mUpdatePeriod;
uint32_t mFlags;
+ int mSessionId;
};
diff --git a/include/media/EffectApi.h b/include/media/EffectApi.h
index a1bc3b8..97874f7 100644
--- a/include/media/EffectApi.h
+++ b/include/media/EffectApi.h
@@ -114,7 +114,8 @@
// +---------------------------+-----------+-----------------------------------
// | Volume management | 5..6 | 0 none
// | | | 1 implements volume control
-// | | | 2..3 reserved
+// | | | 2 requires volume indication
+// | | | 3 reserved
// +---------------------------+-----------+-----------------------------------
// | Device management | 7..8 | 0 none
// | | | 1 requires device updates
@@ -154,6 +155,7 @@
// volume control
#define EFFECT_FLAG_VOLUME_MASK 0x00000060
#define EFFECT_FLAG_VOLUME_CTRL 0x00000020
+#define EFFECT_FLAG_VOLUME_IND 0x00000040
#define EFFECT_FLAG_VOLUME_NONE 0x00000000
// device control
@@ -296,10 +298,12 @@
// | Set and get volume. Used by | EFFECT_CMD_SET_VOLUME | size: n * sizeof(uint32_t) | size: n * sizeof(uint32_t)
// | audio framework to delegate | | data: volume for each channel | data: volume for each channel
// | volume control to effect engine| | defined in effect_config_t in | defined in effect_config_t in
-// | The engine must return the | | 8.24 fixed point format | 8.24 fixed point format
-// | volume that should be applied | | |
-// | before the effect is processed | | |
-// | The overall volume (the volume | | |
+// | If volume control flag is set | | 8.24 fixed point format | 8.24 fixed point format
+// | in the effect descriptor, the | | | It is legal to receive a null
+// | effect engine must return the | | | pointer as pReplyData in which
+// | volume that should be applied | | | case the effect framework has
+// | before the effect is processed | | | delegated volume control to
+// | The overall volume (the volume | | | another effect.
// | actually applied by the effect | | |
// | multiplied by the returned | | |
// | value) should match the | | |
@@ -370,7 +374,7 @@
// structure that defines both input and output buffer configurations and is
// passed by the EFFECT_CMD_CONFIGURE command.
typedef struct buffer_config_s {
- audio_buffer_t buffer; // buffer for use by process() function is not passed explicitly
+ audio_buffer_t buffer; // buffer for use by process() function if not passed explicitly
uint32_t samplingRate; // sampling rate
uint32_t channels; // channel mask (see audio_channels_e in AudioCommon.h)
buffer_provider_t bufferProvider; // buffer provider
@@ -457,7 +461,7 @@
//
// Function: EffectQueryNumberEffects
//
-// Description: Returns the number of different effect exposed by the
+// Description: Returns the number of different effects exposed by the
// library. Each effect must have a unique effect uuid (see
// effect_descriptor_t). This function together with EffectQueryNext()
// is used to enumerate all effects present in the library.
@@ -475,7 +479,7 @@
// *pNumEffects: updated with number of effects in library
//
////////////////////////////////////////////////////////////////////////////////
-typedef int32_t (*effect_QueryNumberEffects_t)(int32_t *pNumEffects);
+typedef int32_t (*effect_QueryNumberEffects_t)(uint32_t *pNumEffects);
////////////////////////////////////////////////////////////////////////////////
//
@@ -521,7 +525,7 @@
// returned value: 0 successful operation.
// -ENODEV library failed to initialize
// -EINVAL invalid pEffectUuid or pInterface
-// -ENOENT No effect with this uuid found
+// -ENOENT no effect with this uuid found
// *pInterface: updated with the effect interface handle.
//
////////////////////////////////////////////////////////////////////////////////
diff --git a/include/media/EffectFactoryApi.h b/include/media/EffectFactoryApi.h
index 8179c23..6cc9932 100644
--- a/include/media/EffectFactoryApi.h
+++ b/include/media/EffectFactoryApi.h
@@ -34,7 +34,7 @@
//
// Function: EffectQueryNumberEffects
//
-// Description: Returns the number of different effect in all loaded libraries.
+// Description: Returns the number of different effects in all loaded libraries.
// Each effect must have a different effect uuid (see
// effect_descriptor_t). This function together with EffectQueryNext()
// is used to enumerate all effects present in all loaded libraries.
@@ -52,7 +52,7 @@
// *pNumEffects: updated with number of effects in factory
//
////////////////////////////////////////////////////////////////////////////////
-int EffectQueryNumberEffects(int *pNumEffects);
+int EffectQueryNumberEffects(uint32_t *pNumEffects);
////////////////////////////////////////////////////////////////////////////////
//
@@ -98,7 +98,7 @@
// returned value: 0 successful operation.
// -ENODEV factory failed to initialize
// -EINVAL invalid pEffectUuid or pInterface
-// -ENOENT No effect with this uuid found
+// -ENOENT no effect with this uuid found
// *pInterface: updated with the effect interface.
//
////////////////////////////////////////////////////////////////////////////////
@@ -140,7 +140,7 @@
//
// Output:
// returned value: 0 successful operation.
-// -ENODEV Effect factory not initialized or
+// -ENODEV effect factory not initialized or
// library could not be loaded or
// library does not implement required functions
// -EINVAL invalid libPath string or handle
@@ -159,7 +159,7 @@
//
// Output:
// returned value: 0 successful operation.
-// -ENODEV Effect factory not initialized
+// -ENODEV effect factory not initialized
// -ENOENT invalid handle
//
////////////////////////////////////////////////////////////////////////////////
@@ -184,7 +184,7 @@
// returned value: 0 successful operation.
// -ENODEV factory failed to initialize
// -EINVAL invalid pEffectUuid or pDescriptor
-// -ENOENT No effect with this uuid found
+// -ENOENT no effect with this uuid found
// *pDescriptor: updated with the effect descriptor.
//
////////////////////////////////////////////////////////////////////////////////
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index c147632..ccfa530 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -27,6 +27,9 @@
#include <media/IAudioTrack.h>
#include <media/IAudioRecord.h>
#include <media/IAudioFlingerClient.h>
+#include <media/EffectApi.h>
+#include <media/IEffect.h>
+#include <media/IEffectClient.h>
#include <utils/String8.h>
namespace android {
@@ -51,6 +54,7 @@
uint32_t flags,
const sp<IMemory>& sharedBuffer,
int output,
+ int *sessionId,
status_t *status) = 0;
virtual sp<IAudioRecord> openRecord(
@@ -61,6 +65,7 @@
int channelCount,
int frameCount,
uint32_t flags,
+ int *sessionId,
status_t *status) = 0;
/* query the audio hardware state. This state never changes,
@@ -134,6 +139,28 @@
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output) = 0;
virtual unsigned int getInputFramesLost(int ioHandle) = 0;
+
+ virtual int newAudioSessionId() = 0;
+
+ virtual status_t loadEffectLibrary(const char *libPath, int *handle) = 0;
+
+ virtual status_t unloadEffectLibrary(int handle) = 0;
+
+ virtual status_t queryNumberEffects(uint32_t *numEffects) = 0;
+
+ virtual status_t queryNextEffect(effect_descriptor_t *pDescriptor) = 0;
+
+ virtual status_t getEffectDescriptor(effect_uuid_t *pEffectUUID, effect_descriptor_t *pDescriptor) = 0;
+
+ virtual sp<IEffect> createEffect(pid_t pid,
+ effect_descriptor_t *pDesc,
+ const sp<IEffectClient>& client,
+ int32_t priority,
+ int output,
+ int sessionId,
+ status_t *status,
+ int *id,
+ int *enabled) = 0;
};
diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h
index de6426a..47d530b 100644
--- a/include/media/IAudioTrack.h
+++ b/include/media/IAudioTrack.h
@@ -62,6 +62,11 @@
*/
virtual void pause() = 0;
+ /* Attach track auxiliary output to specified effect. Use effectId = 0
+ * to detach track from effect.
+ */
+ virtual status_t attachAuxEffect(int effectId) = 0;
+
/* get this tracks control block */
virtual sp<IMemory> getCblk() const = 0;
};
diff --git a/include/private/media/AudioEffectShared.h b/include/private/media/AudioEffectShared.h
new file mode 100644
index 0000000..a3a99a4
--- /dev/null
+++ b/include/private/media/AudioEffectShared.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef ANDROID_EFFECTCBASESHARED_H
+#define ANDROID_EFFECTCBASESHARED_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/threads.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+// Size of buffer used to exchange parameters between application and mediaserver processes.
+#define EFFECT_PARAM_BUFFER_SIZE 1024
+
+
+// Shared memory area used to exchange parameters between application and mediaserver
+// process.
+struct effect_param_cblk_t
+{
+ Mutex lock;
+ volatile uint32_t clientIndex; // Current read/write index for application
+ volatile uint32_t serverIndex; // Current read/write index for mediaserver
+ uint8_t* buffer; // start of parameter buffer
+
+ effect_param_cblk_t()
+ : lock(Mutex::SHARED), clientIndex(0), serverIndex(0) {}
+};
+
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_EFFECTCBASESHARED_H
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index cd47fdf..1510f87 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -78,7 +78,8 @@
uint16_t bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger
uint16_t waitTimeMs; // Cumulated wait time
- uint32_t reserved;
+ uint16_t sendLevel;
+ uint16_t reserved;
// Cache line boundary (32 bytes)
audio_track_cblk_t();
uint32_t stepUser(uint32_t frameCount);
diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk
index 870c0b8..22ecc54 100644
--- a/libs/audioflinger/Android.mk
+++ b/libs/audioflinger/Android.mk
@@ -87,7 +87,8 @@
libutils \
libbinder \
libmedia \
- libhardware_legacy
+ libhardware_legacy \
+ libeffects
ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 3b38d83..1860793 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -37,7 +37,7 @@
#include <media/AudioRecord.h>
#include <private/media/AudioTrackShared.h>
-
+#include <private/media/AudioEffectShared.h>
#include <hardware_legacy/AudioHardwareInterface.h>
#include "AudioMixer.h"
@@ -51,6 +51,8 @@
#include "lifevibes.h"
#endif
+#include <media/EffectFactoryApi.h>
+
// ----------------------------------------------------------------------------
// the sim build doesn't have gettid
@@ -67,6 +69,7 @@
//static const nsecs_t kStandbyTimeInNsecs = seconds(3);
static const float MAX_GAIN = 4096.0f;
+static const float MAX_GAIN_INT = 0x1000;
// retry counts for buffer fill timeout
// 50 * ~20msecs = 1 second
@@ -123,7 +126,7 @@
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
- mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextThreadId(0)
+ mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
{
mHardwareStatus = AUDIO_HW_IDLE;
@@ -282,6 +285,7 @@
uint32_t flags,
const sp<IMemory>& sharedBuffer,
int output,
+ int *sessionId,
status_t *status)
{
sp<PlaybackThread::Track> track;
@@ -289,6 +293,7 @@
sp<Client> client;
wp<Client> wclient;
status_t lStatus;
+ int lSessionId;
if (streamType >= AudioSystem::NUM_STREAM_TYPES) {
LOGE("invalid stream type");
@@ -313,8 +318,23 @@
client = new Client(this, pid);
mClients.add(pid, client);
}
+
+ // If no audio session id is provided, create one here
+ // TODO: enforce same stream type for all tracks in same audio session?
+ // TODO: prevent same audio session on different output threads
+ LOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
+ if (sessionId != NULL && *sessionId != 0) {
+ lSessionId = *sessionId;
+ } else {
+ lSessionId = nextUniqueId();
+ if (sessionId != NULL) {
+ *sessionId = lSessionId;
+ }
+ }
+ LOGV("createTrack() lSessionId: %d", lSessionId);
+
track = thread->createTrack_l(client, streamType, sampleRate, format,
- channelCount, frameCount, sharedBuffer, &lStatus);
+ channelCount, frameCount, sharedBuffer, lSessionId, &lStatus);
}
if (lStatus == NO_ERROR) {
trackHandle = new TrackHandle(track);
@@ -940,10 +960,11 @@
// ----------------------------------------------------------------------------
-AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
+AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
: ThreadBase(audioFlinger, id),
mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output),
- mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
+ mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
+ mDevice(device)
{
readOutputParameters();
@@ -965,6 +986,7 @@
{
dumpInternals(fd, args);
dumpTracks(fd, args);
+ dumpEffectChains(fd, args);
return NO_ERROR;
}
@@ -976,7 +998,7 @@
snprintf(buffer, SIZE, "Output thread %p tracks\n", this);
result.append(buffer);
- result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n");
+ result.append(" Name Clien Typ Fmt Chn Session Buf S M F SRate LeftV RighV Serv User Main buf Aux Buf\n");
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> track = mTracks[i];
if (track != 0) {
@@ -987,7 +1009,7 @@
snprintf(buffer, SIZE, "Output thread %p active tracks\n", this);
result.append(buffer);
- result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n");
+ result.append(" Name Clien Typ Fmt Chn Session Buf S M F SRate LeftV RighV Serv User Main buf Aux Buf\n");
for (size_t i = 0; i < mActiveTracks.size(); ++i) {
wp<Track> wTrack = mActiveTracks[i];
if (wTrack != 0) {
@@ -1002,6 +1024,24 @@
return NO_ERROR;
}
+status_t AudioFlinger::PlaybackThread::dumpEffectChains(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "\n- %d Effect Chains:\n", mEffectChains.size());
+ write(fd, buffer, strlen(buffer));
+
+ for (size_t i = 0; i < mEffectChains.size(); ++i) {
+ sp<EffectChain> chain = mEffectChains[i];
+ if (chain != 0) {
+ chain->dump(fd, args);
+ }
+ }
+ return NO_ERROR;
+}
+
status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
@@ -1020,6 +1060,8 @@
result.append(buffer);
snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended);
result.append(buffer);
+ snprintf(buffer, SIZE, "mix buffer : %p\n", mMixBuffer);
+ result.append(buffer);
write(fd, result.string(), result.size());
dumpBase(fd, args);
@@ -1057,6 +1099,7 @@
int channelCount,
int frameCount,
const sp<IMemory>& sharedBuffer,
+ int sessionId,
status_t *status)
{
sp<Track> track;
@@ -1087,12 +1130,18 @@
{ // scope for mLock
Mutex::Autolock _l(mLock);
track = new Track(this, client, streamType, sampleRate, format,
- channelCount, frameCount, sharedBuffer);
+ channelCount, frameCount, sharedBuffer, sessionId);
if (track->getCblk() == NULL || track->name() < 0) {
lStatus = NO_MEMORY;
goto Exit;
}
mTracks.add(track);
+
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ if (chain != 0) {
+ LOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
+ track->setMainBuffer(chain->inBuffer());
+ }
}
lStatus = NO_ERROR;
@@ -1209,6 +1258,14 @@
track->mFillingUpStatus = Track::FS_FILLING;
track->mResetDone = false;
mActiveTracks.add(track);
+ if (track->mainBuffer() != mMixBuffer) {
+ sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+ if (chain != 0) {
+ LOGV("addTrack_l() starting track on chain %p for session %d", chain.get(), track->sessionId());
+ chain->startTrack();
+ }
+ }
+
status = NO_ERROR;
}
@@ -1271,9 +1328,11 @@
// FIXME - Current mixer implementation only supports stereo output: Always
// Allocate a stereo buffer even if HW output is mono.
- if (mMixBuffer != NULL) delete mMixBuffer;
+ if (mMixBuffer != NULL) delete[] mMixBuffer;
mMixBuffer = new int16_t[mFrameCount * 2];
memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
+
+ //TODO handle effects reconfig
}
status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
@@ -1289,10 +1348,47 @@
return mOutput->getRenderPosition(dspFrames);
}
+bool AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
+{
+ Mutex::Autolock _l(mLock);
+ if (getEffectChain_l(sessionId) != 0) {
+ return true;
+ }
+
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (sessionId == track->sessionId()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain(int sessionId)
+{
+ Mutex::Autolock _l(mLock);
+ return getEffectChain_l(sessionId);
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain_l(int sessionId)
+{
+ sp<EffectChain> chain;
+
+ size_t size = mEffectChains.size();
+ for (size_t i = 0; i < size; i++) {
+ if (mEffectChains[i]->sessionId() == sessionId) {
+ chain = mEffectChains[i];
+ break;
+ }
+ }
+ return chain;
+}
+
// ----------------------------------------------------------------------------
-AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
- : PlaybackThread(audioFlinger, output, id),
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
+ : PlaybackThread(audioFlinger, output, id, device),
mAudioMixer(0)
{
mType = PlaybackThread::MIXER;
@@ -1311,7 +1407,6 @@
bool AudioFlinger::MixerThread::threadLoop()
{
- int16_t* curBuf = mMixBuffer;
Vector< sp<Track> > tracksToRemove;
uint32_t mixerStatus = MIXER_IDLE;
nsecs_t standbyTime = systemTime();
@@ -1324,6 +1419,7 @@
uint32_t activeSleepTime = activeSleepTimeUs();
uint32_t idleSleepTime = idleSleepTimeUs();
uint32_t sleepTime = idleSleepTime;
+ Vector< sp<EffectChain> > effectChains;
while (!exitPending())
{
@@ -1382,13 +1478,20 @@
}
mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
+
+ // prevent any changes in effect chain list and in each effect chain
+ // during mixing and effect process as the audio buffers could be deleted
+ // or modified if an effect is created or deleted
+ effectChains = mEffectChains;
+ lockEffectChains_l();
}
if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
// mix buffers...
- mAudioMixer->process(curBuf);
+ mAudioMixer->process();
sleepTime = 0;
standbyTime = systemTime() + kStandbyTimeInNsecs;
+ //TODO: delay standby when effects have a tail
} else {
// If no tracks are ready, sleep once for the duration of an output
// buffer size, then write 0s to the output
@@ -1400,10 +1503,11 @@
}
} else if (mBytesWritten != 0 ||
(mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) {
- memset (curBuf, 0, mixBufferSize);
+ memset (mMixBuffer, 0, mixBufferSize);
sleepTime = 0;
LOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
}
+ // TODO add standby time extension fct of effect tail
}
if (mSuspended) {
@@ -1411,16 +1515,22 @@
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
- mLastWriteTime = systemTime();
- mInWrite = true;
- mBytesWritten += mixBufferSize;
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+ // enable changes in effect chain
+ unlockEffectChains();
#ifdef LVMX
int audioOutputType = LifeVibes::getMixerType(mId, mType);
if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
- LifeVibes::process(audioOutputType, curBuf, mixBufferSize);
+ LifeVibes::process(audioOutputType, mMixBuffer, mixBufferSize);
}
#endif
- int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize);
+ mLastWriteTime = systemTime();
+ mInWrite = true;
+ mBytesWritten += mixBufferSize;
+
+ int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
mNumWrites++;
mInWrite = false;
@@ -1439,6 +1549,8 @@
}
mStandby = false;
} else {
+ // enable changes in effect chain
+ unlockEffectChains();
usleep(sleepTime);
}
@@ -1446,6 +1558,10 @@
// since we can't guarantee the destructors won't acquire that
// same lock.
tracksToRemove.clear();
+
+ // Effect chains will be actually deleted here if they were removed from
+ // mEffectChains list during mixing or effects processing
+ effectChains.clear();
}
if (!mStandby) {
@@ -1463,6 +1579,8 @@
uint32_t mixerStatus = MIXER_IDLE;
// find out which tracks need to be processed
size_t count = activeTracks.size();
+ size_t mixedTracks = 0;
+ size_t tracksWithEffect = 0;
float masterVolume = mMasterVolume;
bool masterMute = mMasterMute;
@@ -1485,6 +1603,14 @@
LifeVibes::computeVolumes(audioOutputType, activeTypes, tracksConnectedChanged, stateChanged, masterVolume, masterMute);
}
#endif
+ // Delegate master volume control to effect in output mix effect chain if needed
+ sp<EffectChain> chain = getEffectChain_l(0);
+ if (chain != 0) {
+ uint32_t v = (uint32_t)(masterVolume * (1 << 24));
+ chain->setVolume(&v, &v);
+ masterVolume = (float)((v + (1 << 23)) >> 24);
+ chain.clear();
+ }
for (size_t i=0 ; i<count ; i++) {
sp<Track> t = activeTracks[i].promote();
@@ -1501,11 +1627,42 @@
{
//LOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this);
+ mixedTracks++;
+
+ // track->mainBuffer() != mMixBuffer means there is an effect chain
+ // connected to the track
+ chain.clear();
+ if (track->mainBuffer() != mMixBuffer) {
+ chain = getEffectChain_l(track->sessionId());
+ // Delegate volume control to effect in track effect chain if needed
+ if (chain != 0) {
+ tracksWithEffect++;
+ } else {
+ LOGW("prepareTracks_l(): track %08x attached to effect but no chain found on session %d",
+ track->name(), track->sessionId());
+ }
+ }
+
+
+ int param = AudioMixer::VOLUME;
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ // no ramp for the first volume setting
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ param = AudioMixer::RAMP_VOLUME;
+ }
+ } else if (cblk->server != 0) {
+ // If the track is stopped before the first frame was mixed,
+ // do not apply ramp
+ param = AudioMixer::RAMP_VOLUME;
+ }
+
// compute volume for this track
- int16_t left, right;
+ int16_t left, right, aux;
if (track->isMuted() || masterMute || track->isPausing() ||
mStreamTypes[track->type()].mute) {
- left = right = 0;
+ left = right = aux = 0;
if (track->isPausing()) {
track->setPaused();
}
@@ -1524,31 +1681,28 @@
}
#endif
float v = masterVolume * typeVolume;
- float v_clamped = v * cblk->volume[0];
- if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
- left = int16_t(v_clamped);
- v_clamped = v * cblk->volume[1];
- if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
- right = int16_t(v_clamped);
- }
+ uint32_t vl = (uint32_t)(v * cblk->volume[0]) << 12;
+ uint32_t vr = (uint32_t)(v * cblk->volume[1]) << 12;
- // XXX: these things DON'T need to be done each time
- mAudioMixer->setBufferProvider(track);
- mAudioMixer->enable(AudioMixer::MIXING);
-
- int param = AudioMixer::VOLUME;
- if (track->mFillingUpStatus == Track::FS_FILLED) {
- // no ramp for the first volume setting
- track->mFillingUpStatus = Track::FS_ACTIVE;
- if (track->mState == TrackBase::RESUMING) {
- track->mState = TrackBase::ACTIVE;
- param = AudioMixer::RAMP_VOLUME;
+ // Delegate volume control to effect in track effect chain if needed
+ if (chain != 0 && chain->setVolume(&vl, &vr)) {
+ // Do not ramp volume is volume is controlled by effect
+ param = AudioMixer::VOLUME;
}
- } else if (cblk->server != 0) {
- // If the track is stopped before the first frame was mixed,
- // do not apply ramp
- param = AudioMixer::RAMP_VOLUME;
+
+ // Convert volumes from 8.24 to 4.12 format
+ uint32_t v_clamped = (vl + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ left = int16_t(v_clamped);
+ v_clamped = (vr + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ right = int16_t(v_clamped);
+
+ v_clamped = (uint32_t)(v * cblk->sendLevel);
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ aux = int16_t(v_clamped);
}
+
#ifdef LVMX
if ( tracksConnectedChanged || stateChanged )
{
@@ -1556,18 +1710,30 @@
param = AudioMixer::VOLUME;
}
#endif
- mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left);
- mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right);
+
+ // XXX: these things DON'T need to be done each time
+ mAudioMixer->setBufferProvider(track);
+ mAudioMixer->enable(AudioMixer::MIXING);
+
+ mAudioMixer->setParameter(param, AudioMixer::VOLUME0, (void *)left);
+ mAudioMixer->setParameter(param, AudioMixer::VOLUME1, (void *)right);
+ mAudioMixer->setParameter(param, AudioMixer::AUXLEVEL, (void *)aux);
mAudioMixer->setParameter(
AudioMixer::TRACK,
- AudioMixer::FORMAT, track->format());
+ AudioMixer::FORMAT, (void *)track->format());
mAudioMixer->setParameter(
AudioMixer::TRACK,
- AudioMixer::CHANNEL_COUNT, track->channelCount());
+ AudioMixer::CHANNEL_COUNT, (void *)track->channelCount());
mAudioMixer->setParameter(
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
- int(cblk->sampleRate));
+ (void *)(cblk->sampleRate));
+ mAudioMixer->setParameter(
+ AudioMixer::TRACK,
+ AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
+ mAudioMixer->setParameter(
+ AudioMixer::TRACK,
+ AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());
// reset retry count
track->mRetryCount = kMaxTrackRetries;
@@ -1581,7 +1747,6 @@
// We have consumed all the buffers of this track.
// Remove it from the list of active tracks.
tracksToRemove->add(track);
- mAudioMixer->disable(AudioMixer::MIXING);
} else {
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
@@ -1591,9 +1756,8 @@
} else if (mixerStatus != MIXER_TRACKS_READY) {
mixerStatus = MIXER_TRACKS_ENABLED;
}
-
- mAudioMixer->disable(AudioMixer::MIXING);
}
+ mAudioMixer->disable(AudioMixer::MIXING);
}
}
@@ -1603,6 +1767,13 @@
for (size_t i=0 ; i<count ; i++) {
const sp<Track>& track = tracksToRemove->itemAt(i);
mActiveTracks.remove(track);
+ if (track->mainBuffer() != mMixBuffer) {
+ chain = getEffectChain_l(track->sessionId());
+ if (chain != 0) {
+ LOGV("stopping track on chain %p for session Id: %d", chain.get(), track->sessionId());
+ chain->stopTrack();
+ }
+ }
if (track->isTerminated()) {
mTracks.remove(track);
deleteTrackName_l(track->mName);
@@ -1610,6 +1781,13 @@
}
}
+ // mix buffer must be cleared if all tracks are connected to an
+ // effect chain as in this case the mixer will not write to
+ // mix buffer and track effects will accumulate into it
+ if (mixedTracks != 0 && mixedTracks == tracksWithEffect) {
+ memset(mMixBuffer, 0, mFrameCount * mChannelCount * sizeof(int16_t));
+ }
+
return mixerStatus;
}
@@ -1681,6 +1859,15 @@
reconfig = true;
}
}
+ if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
+ // forward device change to effects that have requested to be
+ // aware of attached audio device.
+ mDevice = (uint32_t)value;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice(mDevice);
+ }
+ }
+
if (status == NO_ERROR) {
status = mOutput->setParameters(keyValuePair);
if (!mStandby && status == INVALID_OPERATION) {
@@ -1740,9 +1927,8 @@
}
// ----------------------------------------------------------------------------
-AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
- : PlaybackThread(audioFlinger, output, id),
- mLeftVolume (1.0), mRightVolume(1.0)
+AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
+ : PlaybackThread(audioFlinger, output, id, device)
{
mType = PlaybackThread::DIRECT;
}
@@ -1752,6 +1938,102 @@
}
+static inline int16_t clamp16(int32_t sample)
+{
+ if ((sample>>15) ^ (sample>>31))
+ sample = 0x7FFF ^ (sample>>31);
+ return sample;
+}
+
+static inline
+int32_t mul(int16_t in, int16_t v)
+{
+#if defined(__arm__) && !defined(__thumb__)
+ int32_t out;
+ asm( "smulbb %[out], %[in], %[v] \n"
+ : [out]"=r"(out)
+ : [in]"%r"(in), [v]"r"(v)
+ : );
+ return out;
+#else
+ return in * int32_t(v);
+#endif
+}
+
+void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp)
+{
+ // Do not apply volume on compressed audio
+ if (!AudioSystem::isLinearPCM(mFormat)) {
+ return;
+ }
+
+ // convert to signed 16 bit before volume calculation
+ if (mFormat == AudioSystem::PCM_8_BIT) {
+ size_t count = mFrameCount * mChannelCount;
+ uint8_t *src = (uint8_t *)mMixBuffer + count-1;
+ int16_t *dst = mMixBuffer + count-1;
+ while(count--) {
+ *dst-- = (int16_t)(*src--^0x80) << 8;
+ }
+ }
+
+ size_t frameCount = mFrameCount;
+ int16_t *out = mMixBuffer;
+ if (ramp) {
+ if (mChannelCount == 1) {
+ int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16;
+ int32_t vlInc = d / (int32_t)frameCount;
+ int32_t vl = ((int32_t)mLeftVolShort << 16);
+ do {
+ out[0] = clamp16(mul(out[0], vl >> 16) >> 12);
+ out++;
+ vl += vlInc;
+ } while (--frameCount);
+
+ } else {
+ int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16;
+ int32_t vlInc = d / (int32_t)frameCount;
+ d = ((int32_t)rightVol - (int32_t)mRightVolShort) << 16;
+ int32_t vrInc = d / (int32_t)frameCount;
+ int32_t vl = ((int32_t)mLeftVolShort << 16);
+ int32_t vr = ((int32_t)mRightVolShort << 16);
+ do {
+ out[0] = clamp16(mul(out[0], vl >> 16) >> 12);
+ out[1] = clamp16(mul(out[1], vr >> 16) >> 12);
+ out += 2;
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+ }
+ } else {
+ if (mChannelCount == 1) {
+ do {
+ out[0] = clamp16(mul(out[0], leftVol) >> 12);
+ out++;
+ } while (--frameCount);
+ } else {
+ do {
+ out[0] = clamp16(mul(out[0], leftVol) >> 12);
+ out[1] = clamp16(mul(out[1], rightVol) >> 12);
+ out += 2;
+ } while (--frameCount);
+ }
+ }
+
+ // convert back to unsigned 8 bit after volume calculation
+ if (mFormat == AudioSystem::PCM_8_BIT) {
+ size_t count = mFrameCount * mChannelCount;
+ int16_t *src = mMixBuffer;
+ uint8_t *dst = (uint8_t *)mMixBuffer;
+ while(count--) {
+ *dst++ = (uint8_t)(((int32_t)*src++ + (1<<7)) >> 8)^0x80;
+ }
+ }
+
+ mLeftVolShort = leftVol;
+ mRightVolShort = rightVol;
+}
+
bool AudioFlinger::DirectOutputThread::threadLoop()
{
uint32_t mixerStatus = MIXER_IDLE;
@@ -1770,6 +2052,11 @@
while (!exitPending())
{
+ bool rampVolume;
+ uint16_t leftVol;
+ uint16_t rightVol;
+ Vector< sp<EffectChain> > effectChains;
+
processConfigEvents();
mixerStatus = MIXER_IDLE;
@@ -1821,6 +2108,8 @@
}
}
+ effectChains = mEffectChains;
+
// find out which tracks need to be processed
if (mActiveTracks.size() != 0) {
sp<Track> t = mActiveTracks[0].promote();
@@ -1836,6 +2125,19 @@
{
//LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ mLeftVolFloat = mRightVolFloat = 0;
+ mLeftVolShort = mRightVolShort = 0;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ rampVolume = true;
+ }
+ } else if (cblk->server != 0) {
+ // If the track is stopped before the first frame was mixed,
+ // do not apply ramp
+ rampVolume = true;
+ }
// compute volume for this track
float left, right;
if (track->isMuted() || mMasterMute || track->isPausing() ||
@@ -1855,17 +2157,42 @@
right = v_clamped/MAX_GAIN;
}
- if (left != mLeftVolume || right != mRightVolume) {
- mOutput->setVolume(left, right);
- left = mLeftVolume;
- right = mRightVolume;
- }
+ if (left != mLeftVolFloat || right != mRightVolFloat) {
+ mLeftVolFloat = left;
+ mRightVolFloat = right;
- if (track->mFillingUpStatus == Track::FS_FILLED) {
- track->mFillingUpStatus = Track::FS_ACTIVE;
- if (track->mState == TrackBase::RESUMING) {
- track->mState = TrackBase::ACTIVE;
+ // If audio HAL implements volume control,
+ // force software volume to nominal value
+ if (mOutput->setVolume(left, right) == NO_ERROR) {
+ left = 1.0f;
+ right = 1.0f;
}
+
+ // Convert volumes from float to 8.24
+ uint32_t vl = (uint32_t)(left * (1 << 24));
+ uint32_t vr = (uint32_t)(right * (1 << 24));
+
+ // Delegate volume control to effect in track effect chain if needed
+ // only one effect chain can be present on DirectOutputThread, so if
+ // there is one, the track is connected to it
+ if (!effectChains.isEmpty()) {
+ // Do not ramp volume is volume is controlled by effect
+ if(effectChains[0]->setVolume(&vl, &vr)) {
+ rampVolume = false;
+ }
+ }
+
+ // Convert volumes from 8.24 to 4.12 format
+ uint32_t v_clamped = (vl + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ leftVol = (uint16_t)v_clamped;
+ v_clamped = (vr + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ rightVol = (uint16_t)v_clamped;
+ } else {
+ leftVol = mLeftVolShort;
+ rightVol = mRightVolShort;
+ rampVolume = false;
}
// reset retry count
@@ -1897,11 +2224,17 @@
// remove all the tracks that need to be...
if (UNLIKELY(trackToRemove != 0)) {
mActiveTracks.remove(trackToRemove);
+ if (!effectChains.isEmpty()) {
+ LOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(), trackToRemove->sessionId());
+ effectChains[0]->stopTrack();
+ }
if (trackToRemove->isTerminated()) {
mTracks.remove(trackToRemove);
deleteTrackName_l(trackToRemove->mName);
}
}
+
+ lockEffectChains_l();
}
if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
@@ -1909,7 +2242,7 @@
size_t frameCount = mFrameCount;
curBuf = (int8_t *)mMixBuffer;
// output audio to hardware
- while(frameCount) {
+ while (frameCount) {
buffer.frameCount = frameCount;
activeTrack->getNextBuffer(&buffer);
if (UNLIKELY(buffer.raw == 0)) {
@@ -1941,6 +2274,14 @@
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
+ if (mixerStatus == MIXER_TRACKS_READY) {
+ applyVolume(leftVol, rightVol, rampVolume);
+ }
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+ unlockEffectChains();
+
mLastWriteTime = systemTime();
mInWrite = true;
mBytesWritten += mixBufferSize;
@@ -1950,6 +2291,7 @@
mInWrite = false;
mStandby = false;
} else {
+ unlockEffectChains();
usleep(sleepTime);
}
@@ -1958,6 +2300,10 @@
// same lock.
trackToRemove.clear();
activeTrack.clear();
+
+ // Effect chains will be actually deleted here if they were removed from
+ // mEffectChains list during mixing or effects processing
+ effectChains.clear();
}
if (!mStandby) {
@@ -2048,7 +2394,7 @@
// ----------------------------------------------------------------------------
AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
- : MixerThread(audioFlinger, mainThread->getOutput(), id), mWaitTimeMs(UINT_MAX)
+ : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device()), mWaitTimeMs(UINT_MAX)
{
mType = PlaybackThread::DUPLICATING;
addOutputTrack(mainThread);
@@ -2064,7 +2410,6 @@
bool AudioFlinger::DuplicatingThread::threadLoop()
{
- int16_t* curBuf = mMixBuffer;
Vector< sp<Track> > tracksToRemove;
uint32_t mixerStatus = MIXER_IDLE;
nsecs_t standbyTime = systemTime();
@@ -2074,6 +2419,7 @@
uint32_t activeSleepTime = activeSleepTimeUs();
uint32_t idleSleepTime = idleSleepTimeUs();
uint32_t sleepTime = idleSleepTime;
+ Vector< sp<EffectChain> > effectChains;
while (!exitPending())
{
@@ -2134,14 +2480,20 @@
}
mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
+
+ // prevent any changes in effect chain list and in each effect chain
+ // during mixing and effect process as the audio buffers could be deleted
+ // or modified if an effect is created or deleted
+ effectChains = mEffectChains;
+ lockEffectChains_l();
}
if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
// mix buffers...
if (outputsReady(outputTracks)) {
- mAudioMixer->process(curBuf);
+ mAudioMixer->process();
} else {
- memset(curBuf, 0, mixBufferSize);
+ memset(mMixBuffer, 0, mixBufferSize);
}
sleepTime = 0;
writeFrames = mFrameCount;
@@ -2158,6 +2510,7 @@
if (outputTracks[i]->isActive()) {
sleepTime = 0;
writeFrames = 0;
+ memset(mMixBuffer, 0, mixBufferSize);
break;
}
}
@@ -2169,13 +2522,21 @@
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+ // enable changes in effect chain
+ unlockEffectChains();
+
standbyTime = systemTime() + kStandbyTimeInNsecs;
for (size_t i = 0; i < outputTracks.size(); i++) {
- outputTracks[i]->write(curBuf, writeFrames);
+ outputTracks[i]->write(mMixBuffer, writeFrames);
}
mStandby = false;
mBytesWritten += mixBufferSize;
} else {
+ // enable changes in effect chain
+ unlockEffectChains();
usleep(sleepTime);
}
@@ -2184,6 +2545,10 @@
// same lock.
tracksToRemove.clear();
outputTracks.clear();
+
+ // Effect chains will be actually deleted here if they were removed from
+ // mEffectChains list during mixing or effects processing
+ effectChains.clear();
}
return false;
@@ -2268,7 +2633,8 @@
int channelCount,
int frameCount,
uint32_t flags,
- const sp<IMemory>& sharedBuffer)
+ const sp<IMemory>& sharedBuffer,
+ int sessionId)
: RefBase(),
mThread(thread),
mClient(client),
@@ -2277,7 +2643,8 @@
mState(IDLE),
mClientTid(-1),
mFormat(format),
- mFlags(flags & ~SYSTEM_FLAGS_MASK)
+ mFlags(flags & ~SYSTEM_FLAGS_MASK),
+ mSessionId(sessionId)
{
LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
@@ -2420,15 +2787,17 @@
int format,
int channelCount,
int frameCount,
- const sp<IMemory>& sharedBuffer)
- : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer),
- mMute(false), mSharedBuffer(sharedBuffer), mName(-1)
+ const sp<IMemory>& sharedBuffer,
+ int sessionId)
+ : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer, sessionId),
+ mMute(false), mSharedBuffer(sharedBuffer), mName(-1), mMainBuffer(NULL), mAuxBuffer(NULL), mAuxEffectId(0)
{
if (mCblk != NULL) {
sp<ThreadBase> baseThread = thread.promote();
if (baseThread != 0) {
PlaybackThread *playbackThread = (PlaybackThread *)baseThread.get();
mName = playbackThread->getTrackName_l();
+ mMainBuffer = playbackThread->mixBuffer();
}
LOGV("Track constructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid());
if (mName < 0) {
@@ -2482,12 +2851,13 @@
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
{
- snprintf(buffer, size, " %5d %5d %3u %3u %3u %04u %1d %1d %1d %5u %5u %5u %08x %08x\n",
+ snprintf(buffer, size, " %05d %05d %03u %03u %03u %05u %04u %1d %1d %1d %05u %05u %05u 0x%08x 0x%08x 0x%08x 0x%08x\n",
mName - AudioMixer::TRACK0,
(mClient == NULL) ? getpid() : mClient->pid(),
mStreamType,
mFormat,
mCblk->channelCount,
+ mSessionId,
mFrameCount,
mState,
mMute,
@@ -2496,7 +2866,9 @@
mCblk->volume[0],
mCblk->volume[1],
mCblk->server,
- mCblk->user);
+ mCblk->user,
+ (int)mMainBuffer,
+ (int)mAuxBuffer);
}
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
@@ -2679,6 +3051,23 @@
mVolume[1] = right;
}
+status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
+{
+ status_t status = DEAD_OBJECT;
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ status = playbackThread->attachAuxEffect(this, EffectId);
+ }
+ return status;
+}
+
+void AudioFlinger::PlaybackThread::Track::setAuxBuffer(int EffectId, int32_t *buffer)
+{
+ mAuxEffectId = EffectId;
+ mAuxBuffer = buffer;
+}
+
// ----------------------------------------------------------------------------
// RecordTrack constructor must be called with AudioFlinger::mLock held
@@ -2689,9 +3078,10 @@
int format,
int channelCount,
int frameCount,
- uint32_t flags)
+ uint32_t flags,
+ int sessionId)
: TrackBase(thread, client, sampleRate, format,
- channelCount, frameCount, flags, 0),
+ channelCount, frameCount, flags, 0, sessionId),
mOverflow(false)
{
if (mCblk != NULL) {
@@ -2779,10 +3169,11 @@
void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
{
- snprintf(buffer, size, " %05d %03u %03u %04u %01d %05u %08x %08x\n",
+ snprintf(buffer, size, " %05d %03u %03u %05d %04u %01d %05u %08x %08x\n",
(mClient == NULL) ? getpid() : mClient->pid(),
mFormat,
mCblk->channelCount,
+ mSessionId,
mFrameCount,
mState,
mCblk->sampleRate,
@@ -2800,7 +3191,7 @@
int format,
int channelCount,
int frameCount)
- : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL),
+ : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL, 0),
mActive(false), mSourceThread(sourceThread)
{
@@ -3115,6 +3506,11 @@
return mTrack->getCblk();
}
+status_t AudioFlinger::TrackHandle::attachAuxEffect(int EffectId)
+{
+ return mTrack->attachAuxEffect(EffectId);
+}
+
status_t AudioFlinger::TrackHandle::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -3131,6 +3527,7 @@
int channelCount,
int frameCount,
uint32_t flags,
+ int *sessionId,
status_t *status)
{
sp<RecordThread::RecordTrack> recordTrack;
@@ -3140,6 +3537,7 @@
status_t lStatus;
RecordThread *thread;
size_t inFrameCount;
+ int lSessionId;
// check calling permissions
if (!recordingAllowed()) {
@@ -3164,9 +3562,18 @@
mClients.add(pid, client);
}
+ // If no audio session id is provided, create one here
+ if (sessionId != NULL && *sessionId != 0) {
+ lSessionId = *sessionId;
+ } else {
+ lSessionId = nextUniqueId();
+ if (sessionId != NULL) {
+ *sessionId = lSessionId;
+ }
+ }
// create new record track. The record track uses one track in mHardwareMixerThread by convention.
recordTrack = new RecordThread::RecordTrack(thread, client, sampleRate,
- format, channelCount, frameCount, flags);
+ format, channelCount, frameCount, flags, lSessionId);
}
if (recordTrack->getCblk() == NULL) {
// remove local strong reference to Client before deleting the RecordTrack so that the Client
@@ -3504,7 +3911,7 @@
if (mActiveTrack != 0) {
result.append("Active Track:\n");
- result.append(" Clien Fmt Chn Buf S SRate Serv User\n");
+ result.append(" Clien Fmt Chn Session Buf S SRate Serv User\n");
mActiveTrack->dump(buffer, SIZE);
result.append(buffer);
@@ -3753,14 +4160,15 @@
mHardwareStatus = AUDIO_HW_IDLE;
if (output != 0) {
+ int id = nextUniqueId();
if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
(format != AudioSystem::PCM_16_BIT) ||
(channels != AudioSystem::CHANNEL_OUT_STEREO)) {
- thread = new DirectOutputThread(this, output, ++mNextThreadId);
- LOGV("openOutput() created direct output: ID %d thread %p", mNextThreadId, thread);
+ thread = new DirectOutputThread(this, output, id, *pDevices);
+ LOGV("openOutput() created direct output: ID %d thread %p", id, thread);
} else {
- thread = new MixerThread(this, output, ++mNextThreadId);
- LOGV("openOutput() created mixer output: ID %d thread %p", mNextThreadId, thread);
+ thread = new MixerThread(this, output, id, *pDevices);
+ LOGV("openOutput() created mixer output: ID %d thread %p", id, thread);
#ifdef LVMX
unsigned bitsPerSample =
@@ -3774,7 +4182,7 @@
#endif
}
- mPlaybackThreads.add(mNextThreadId, thread);
+ mPlaybackThreads.add(id, thread);
if (pSamplingRate) *pSamplingRate = samplingRate;
if (pFormat) *pFormat = format;
@@ -3783,7 +4191,7 @@
// notify client processes of the new output creation
thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
- return mNextThreadId;
+ return id;
}
return 0;
@@ -3800,13 +4208,13 @@
return 0;
}
-
- DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId);
+ int id = nextUniqueId();
+ DuplicatingThread *thread = new DuplicatingThread(this, thread1, id);
thread->addOutputTrack(thread2);
- mPlaybackThreads.add(mNextThreadId, thread);
+ mPlaybackThreads.add(id, thread);
// notify client processes of the new output creation
thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
- return mNextThreadId;
+ return id;
}
status_t AudioFlinger::closeOutput(int output)
@@ -3925,10 +4333,11 @@
}
if (input != 0) {
+ int id = nextUniqueId();
// Start record thread
- thread = new RecordThread(this, input, reqSamplingRate, reqChannels, ++mNextThreadId);
- mRecordThreads.add(mNextThreadId, thread);
- LOGV("openInput() created record thread: ID %d thread %p", mNextThreadId, thread);
+ thread = new RecordThread(this, input, reqSamplingRate, reqChannels, id);
+ mRecordThreads.add(id, thread);
+ LOGV("openInput() created record thread: ID %d thread %p", id, thread);
if (pSamplingRate) *pSamplingRate = reqSamplingRate;
if (pFormat) *pFormat = format;
if (pChannels) *pChannels = reqChannels;
@@ -3937,7 +4346,7 @@
// notify client processes of the new input creation
thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED);
- return mNextThreadId;
+ return id;
}
return 0;
@@ -3991,6 +4400,12 @@
return NO_ERROR;
}
+
+int AudioFlinger::newAudioSessionId()
+{
+ return nextUniqueId();
+}
+
// checkPlaybackThread_l() must be called with AudioFlinger::mLock held
AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(int output) const
{
@@ -4023,6 +4438,1475 @@
return thread;
}
+int AudioFlinger::nextUniqueId()
+{
+ return android_atomic_inc(&mNextUniqueId);
+}
+
+// ----------------------------------------------------------------------------
+// Effect management
+// ----------------------------------------------------------------------------
+
+
+status_t AudioFlinger::loadEffectLibrary(const char *libPath, int *handle)
+{
+ Mutex::Autolock _l(mLock);
+ return EffectLoadLibrary(libPath, handle);
+}
+
+status_t AudioFlinger::unloadEffectLibrary(int handle)
+{
+ Mutex::Autolock _l(mLock);
+ return EffectUnloadLibrary(handle);
+}
+
+status_t AudioFlinger::queryNumberEffects(uint32_t *numEffects)
+{
+ Mutex::Autolock _l(mLock);
+ return EffectQueryNumberEffects(numEffects);
+}
+
+status_t AudioFlinger::queryNextEffect(effect_descriptor_t *descriptor)
+{
+ Mutex::Autolock _l(mLock);
+ return EffectQueryNext(descriptor);
+}
+
+status_t AudioFlinger::getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor)
+{
+ Mutex::Autolock _l(mLock);
+ return EffectGetDescriptor(pUuid, descriptor);
+}
+
+sp<IEffect> AudioFlinger::createEffect(pid_t pid,
+ effect_descriptor_t *pDesc,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority,
+ int output,
+ int sessionId,
+ status_t *status,
+ int *id,
+ int *enabled)
+{
+ status_t lStatus = NO_ERROR;
+ sp<EffectHandle> handle;
+ effect_interface_t itfe;
+ effect_descriptor_t desc;
+ sp<Client> client;
+ wp<Client> wclient;
+
+ LOGV("createEffect pid %d, client %p, priority %d, sessionId %d, output %d", pid, effectClient.get(), priority, sessionId, output);
+
+ if (pDesc == NULL) {
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ {
+ Mutex::Autolock _l(mLock);
+
+ if (!EffectIsNullUuid(&pDesc->uuid)) {
+ // if uuid is specified, request effect descriptor
+ lStatus = EffectGetDescriptor(&pDesc->uuid, &desc);
+ if (lStatus < 0) {
+ LOGW("createEffect() error %d from EffectGetDescriptor", lStatus);
+ goto Exit;
+ }
+ } else {
+ // if uuid is not specified, look for an available implementation
+ // of the required type in effect factory
+ if (EffectIsNullUuid(&pDesc->type)) {
+ LOGW("createEffect() no effect type");
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ uint32_t numEffects = 0;
+ effect_descriptor_t d;
+ bool found = false;
+
+ lStatus = EffectQueryNumberEffects(&numEffects);
+ if (lStatus < 0) {
+ LOGW("createEffect() error %d from EffectQueryNumberEffects", lStatus);
+ goto Exit;
+ }
+ for (; numEffects > 0; numEffects--) {
+ lStatus = EffectQueryNext(&desc);
+ if (lStatus < 0) {
+ LOGW("createEffect() error %d from EffectQueryNext", lStatus);
+ continue;
+ }
+ if (memcmp(&desc.type, &pDesc->type, sizeof(effect_uuid_t)) == 0) {
+ // If matching type found save effect descriptor. If the session is
+ // 0 and the effect is not auxiliary, continue enumeration in case
+ // an auxiliary version of this effect type is available
+ found = true;
+ memcpy(&d, &desc, sizeof(effect_descriptor_t));
+ if (sessionId != 0 ||
+ (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ break;
+ }
+ }
+ }
+ if (!found) {
+ lStatus = BAD_VALUE;
+ LOGW("createEffect() effect not found");
+ goto Exit;
+ }
+ // For same effect type, chose auxiliary version over insert version if
+ // connect to output mix (Compliance to OpenSL ES)
+ if (sessionId == 0 &&
+ (d.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_AUXILIARY) {
+ memcpy(&desc, &d, sizeof(effect_descriptor_t));
+ }
+ }
+
+ // Do not allow auxiliary effects on a session different from 0 (output mix)
+ if (sessionId != 0 &&
+ (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ lStatus = INVALID_OPERATION;
+ goto Exit;
+ }
+
+ // return effect descriptor
+ memcpy(pDesc, &desc, sizeof(effect_descriptor_t));
+
+ // If output is not specified try to find a matching audio session ID in one of the
+ // output threads.
+ // TODO: allow attachment of effect to inputs
+ if (output == 0) {
+ if (sessionId == 0) {
+ // default to first output
+ // TODO: define criteria to choose output when not specified. Or
+ // receive output from audio policy manager
+ if (mPlaybackThreads.size() != 0) {
+ output = mPlaybackThreads.keyAt(0);
+ }
+ } else {
+ // look for the thread where the specified audio session is present
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId)) {
+ output = mPlaybackThreads.keyAt(i);
+ break;
+ }
+ }
+ }
+ }
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGE("unknown output thread");
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ wclient = mClients.valueFor(pid);
+
+ if (wclient != NULL) {
+ client = wclient.promote();
+ } else {
+ client = new Client(this, pid);
+ mClients.add(pid, client);
+ }
+
+ // create effect on selected output trhead
+ handle = thread->createEffect_l(client, effectClient, priority, sessionId, &desc, enabled, &lStatus);
+ if (handle != 0 && id != NULL) {
+ *id = handle->id();
+ }
+ }
+
+Exit:
+ if(status) {
+ *status = lStatus;
+ }
+ return handle;
+}
+
+// PlaybackThread::createEffect_l() must be called with AudioFlinger::mLock held
+sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l(
+ const sp<AudioFlinger::Client>& client,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority,
+ int sessionId,
+ effect_descriptor_t *desc,
+ int *enabled,
+ status_t *status
+ )
+{
+ sp<EffectModule> effect;
+ sp<EffectHandle> handle;
+ status_t lStatus;
+ sp<Track> track;
+ sp<EffectChain> chain;
+ bool effectCreated = false;
+
+ if (mOutput == 0) {
+ LOGW("createEffect_l() Audio driver not initialized.");
+ lStatus = NO_INIT;
+ goto Exit;
+ }
+
+ // Do not allow auxiliary effect on session other than 0
+ if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY &&
+ sessionId != 0) {
+ LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d", desc->name, sessionId);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ // Do not allow effects with session ID 0 on direct output or duplicating threads
+ // TODO: add rule for hw accelerated effects on direct outputs with non PCM format
+ if (sessionId == 0 && mType != MIXER) {
+ LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d", desc->name, sessionId);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ LOGV("createEffect_l() thread %p effect %s on session %d", this, desc->name, sessionId);
+
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+
+ // check for existing effect chain with the requested audio session
+ chain = getEffectChain_l(sessionId);
+ if (chain == 0) {
+ // create a new chain for this session
+ LOGV("createEffect_l() new effect chain for session %d", sessionId);
+ chain = new EffectChain(this, sessionId);
+ addEffectChain_l(chain);
+ } else {
+ effect = chain->getEffectFromDesc(desc);
+ }
+
+ LOGV("createEffect_l() got effect %p on chain %p", effect == 0 ? 0 : effect.get(), chain.get());
+
+ if (effect == 0) {
+ // create a new effect module if none present in the chain
+ effectCreated = true;
+ effect = new EffectModule(this, chain, desc, mAudioFlinger->nextUniqueId(), sessionId);
+ lStatus = effect->status();
+ if (lStatus != NO_ERROR) {
+ goto Exit;
+ }
+ //TODO: handle CPU load and memory usage here
+ lStatus = chain->addEffect(effect);
+ if (lStatus != NO_ERROR) {
+ goto Exit;
+ }
+
+ effect->setDevice(mDevice);
+ }
+ // create effect handle and connect it to effect module
+ handle = new EffectHandle(effect, client, effectClient, priority);
+ lStatus = effect->addHandle(handle);
+ if (enabled) {
+ *enabled = (int)effect->isEnabled();
+ }
+ }
+
+Exit:
+ if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
+ if (chain != 0 && effectCreated) {
+ if (chain->removeEffect(effect) == 0) {
+ removeEffectChain_l(chain);
+ }
+ }
+ handle.clear();
+ }
+
+ if(status) {
+ *status = lStatus;
+ }
+ return handle;
+}
+
+status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
+{
+ int session = chain->sessionId();
+ int16_t *buffer = mMixBuffer;
+
+ LOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
+ if (session == 0) {
+ chain->setInBuffer(buffer, false);
+ chain->setOutBuffer(buffer);
+ // Effect chain for session 0 is inserted at end of effect chains list
+ // to be processed last as it contains output mix effects to apply after
+ // all track specific effects
+ mEffectChains.add(chain);
+ } else {
+ bool ownsBuffer = false;
+ // Only one effect chain can be present in direct output thread and it uses
+ // the mix buffer as input
+ if (mType != DIRECT) {
+ size_t numSamples = mFrameCount * mChannelCount;
+ buffer = new int16_t[numSamples];
+ memset(buffer, 0, numSamples * sizeof(int16_t));
+ LOGV("addEffectChain_l() creating new input buffer %p session %d", buffer, session);
+ ownsBuffer = true;
+ }
+ chain->setInBuffer(buffer, ownsBuffer);
+ chain->setOutBuffer(mMixBuffer);
+ // Effect chain for session other than 0 is inserted at beginning of effect
+ // chains list to be processed before output mix effects. Relative order between
+ // sessions other than 0 is not important
+ mEffectChains.insertAt(chain, 0);
+ }
+
+ // Attach all tracks with same session ID to this chain.
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (session == track->sessionId()) {
+ LOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(), buffer);
+ track->setMainBuffer(buffer);
+ }
+ }
+
+ // indicate all active tracks in the chain
+ for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
+ sp<Track> track = mActiveTracks[i].promote();
+ if (track == 0) continue;
+ if (session == track->sessionId()) {
+ LOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
+ chain->startTrack();
+ }
+ }
+
+ return NO_ERROR;
+}
+
+size_t AudioFlinger::PlaybackThread::removeEffectChain_l(const sp<EffectChain>& chain)
+{
+ int session = chain->sessionId();
+
+ LOGV("removeEffectChain_l() %p from thread %p for session %d", chain.get(), this, session);
+
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ if (chain == mEffectChains[i]) {
+ mEffectChains.removeAt(i);
+ // detach all tracks with same session ID from this chain
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (session == track->sessionId()) {
+ track->setMainBuffer(mMixBuffer);
+ }
+ }
+ }
+ }
+ return mEffectChains.size();
+}
+
+void AudioFlinger::PlaybackThread::lockEffectChains_l()
+{
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->lock();
+ }
+}
+
+void AudioFlinger::PlaybackThread::unlockEffectChains()
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->unlock();
+ }
+}
+
+sp<AudioFlinger::EffectModule> AudioFlinger::PlaybackThread::getEffect_l(int sessionId, int effectId)
+{
+ sp<EffectModule> effect;
+
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ if (chain != 0) {
+ effect = chain->getEffectFromId(effectId);
+ }
+ return effect;
+}
+
+status_t AudioFlinger::PlaybackThread::attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId)
+{
+ Mutex::Autolock _l(mLock);
+ return attachAuxEffect_l(track, EffectId);
+}
+
+status_t AudioFlinger::PlaybackThread::attachAuxEffect_l(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId)
+{
+ status_t status = NO_ERROR;
+
+ if (EffectId == 0) {
+ track->setAuxBuffer(0, NULL);
+ } else {
+ // Auxiliary effects are always in audio session 0
+ sp<EffectModule> effect = getEffect_l(0, EffectId);
+ if (effect != 0) {
+ if ((effect->desc().flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ track->setAuxBuffer(EffectId, (int32_t *)effect->inBuffer());
+ } else {
+ status = INVALID_OPERATION;
+ }
+ } else {
+ status = BAD_VALUE;
+ }
+ }
+ return status;
+}
+
+void AudioFlinger::PlaybackThread::detachAuxEffect_l(int effectId)
+{
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (track->auxEffectId() == effectId) {
+ attachAuxEffect_l(track, 0);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// EffectModule implementation
+// ----------------------------------------------------------------------------
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectModule"
+
+AudioFlinger::EffectModule::EffectModule(const wp<ThreadBase>& wThread,
+ const wp<AudioFlinger::EffectChain>& chain,
+ effect_descriptor_t *desc,
+ int id,
+ int sessionId)
+ : mThread(wThread), mChain(chain), mId(id), mSessionId(sessionId), mEffectInterface(NULL),
+ mStatus(NO_INIT), mState(IDLE)
+{
+ LOGV("Constructor %p", this);
+ int lStatus;
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ return;
+ }
+ PlaybackThread *p = (PlaybackThread *)thread.get();
+
+ memcpy(&mDescriptor, desc, sizeof(effect_descriptor_t));
+
+ // create effect engine from effect factory
+ mStatus = EffectCreate(&desc->uuid, &mEffectInterface);
+ if (mStatus != NO_ERROR) {
+ return;
+ }
+ lStatus = init();
+ if (lStatus < 0) {
+ mStatus = lStatus;
+ goto Error;
+ }
+
+ LOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface);
+ return;
+Error:
+ EffectRelease(mEffectInterface);
+ mEffectInterface = NULL;
+ LOGV("Constructor Error %d", mStatus);
+}
+
+AudioFlinger::EffectModule::~EffectModule()
+{
+ LOGV("Destructor %p", this);
+ if (mEffectInterface != NULL) {
+ // release effect engine
+ EffectRelease(mEffectInterface);
+ }
+}
+
+status_t AudioFlinger::EffectModule::addHandle(sp<EffectHandle>& handle)
+{
+ status_t status;
+
+ Mutex::Autolock _l(mLock);
+ // First handle in mHandles has highest priority and controls the effect module
+ int priority = handle->priority();
+ size_t size = mHandles.size();
+ sp<EffectHandle> h;
+ size_t i;
+ for (i = 0; i < size; i++) {
+ h = mHandles[i].promote();
+ if (h == 0) continue;
+ if (h->priority() <= priority) break;
+ }
+ // if inserted in first place, move effect control from previous owner to this handle
+ if (i == 0) {
+ if (h != 0) {
+ h->setControl(false, true);
+ }
+ handle->setControl(true, false);
+ status = NO_ERROR;
+ } else {
+ status = ALREADY_EXISTS;
+ }
+ mHandles.insertAt(handle, i);
+ return status;
+}
+
+size_t AudioFlinger::EffectModule::removeHandle(const wp<EffectHandle>& handle)
+{
+ Mutex::Autolock _l(mLock);
+ size_t size = mHandles.size();
+ size_t i;
+ for (i = 0; i < size; i++) {
+ if (mHandles[i] == handle) break;
+ }
+ if (i == size) {
+ return size;
+ }
+ mHandles.removeAt(i);
+ size = mHandles.size();
+ // if removed from first place, move effect control from this handle to next in line
+ if (i == 0 && size != 0) {
+ sp<EffectHandle> h = mHandles[0].promote();
+ if (h != 0) {
+ h->setControl(true, true);
+ }
+ }
+
+ return size;
+}
+
+void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle)
+{
+ // keep a strong reference on this EffectModule to avoid calling the
+ // destructor before we exit
+ sp<EffectModule> keep(this);
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ // delete the effect module if removing last handle on it
+ if (removeHandle(handle) == 0) {
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ playbackThread->detachAuxEffect_l(mId);
+ }
+ sp<EffectChain> chain = mChain.promote();
+ if (chain != 0) {
+ // remove effect chain if remove last effect
+ if (chain->removeEffect(keep) == 0) {
+ playbackThread->removeEffectChain_l(chain);
+ }
+ }
+ }
+ }
+}
+
+void AudioFlinger::EffectModule::process()
+{
+ Mutex::Autolock _l(mLock);
+
+ if (mEffectInterface == NULL || mConfig.inputCfg.buffer.raw == NULL || mConfig.outputCfg.buffer.raw == NULL) {
+ return;
+ }
+
+ if (mState != IDLE) {
+ // do 32 bit to 16 bit conversion for auxiliary effect input buffer
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ AudioMixer::ditherAndClamp(mConfig.inputCfg.buffer.s32,
+ mConfig.inputCfg.buffer.s32,
+ mConfig.inputCfg.buffer.frameCount);
+ }
+
+ // TODO: handle effects with buffer provider
+ if (mState != ACTIVE) {
+ uint32_t count = mConfig.inputCfg.buffer.frameCount;
+ int32_t amp = 32767L << 16;
+ int32_t step = amp / count;
+ int16_t *pIn = mConfig.inputCfg.buffer.s16;
+ int16_t *pOut = mConfig.outputCfg.buffer.s16;
+ int inChannels;
+ int outChannels;
+
+ if (mConfig.inputCfg.channels == CHANNEL_MONO) {
+ inChannels = 1;
+ } else {
+ inChannels = 2;
+ }
+ if (mConfig.outputCfg.channels == CHANNEL_MONO) {
+ outChannels = 1;
+ } else {
+ outChannels = 2;
+ }
+
+ switch (mState) {
+ case RESET:
+ reset();
+ // clear auxiliary effect input buffer for next accumulation
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ memset(mConfig.inputCfg.buffer.raw, 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
+ }
+ step = -step;
+ mState = STARTING;
+ break;
+ case STARTING:
+ start();
+ amp = 0;
+ pOut = mConfig.inputCfg.buffer.s16;
+ outChannels = inChannels;
+ mState = ACTIVE;
+ break;
+ case STOPPING:
+ step = -step;
+ pOut = mConfig.inputCfg.buffer.s16;
+ outChannels = inChannels;
+ mState = STOPPED;
+ break;
+ case STOPPED:
+ stop();
+ amp = 0;
+ mState = IDLE;
+ break;
+ }
+
+ // ramp volume down or up before activating or deactivating the effect
+ if (inChannels == 1) {
+ if (outChannels == 1) {
+ while (count--) {
+ *pOut++ = (int16_t)(((int32_t)*pIn++ * (amp >> 16)) >> 15);
+ amp += step;
+ }
+ } else {
+ while (count--) {
+ int32_t smp = (int16_t)(((int32_t)*pIn++ * (amp >> 16)) >> 15);
+ *pOut++ = smp;
+ *pOut++ = smp;
+ amp += step;
+ }
+ }
+ } else {
+ if (outChannels == 1) {
+ while (count--) {
+ int32_t smp = (((int32_t)*pIn * (amp >> 16)) >> 16) +
+ (((int32_t)*(pIn + 1) * (amp >> 16)) >> 16);
+ pIn += 2;
+ *pOut++ = (int16_t)smp;
+ amp += step;
+ }
+ } else {
+ while (count--) {
+ *pOut++ = (int16_t)((int32_t)*pIn++ * (amp >> 16)) >> 15;
+ *pOut++ = (int16_t)((int32_t)*pIn++ * (amp >> 16)) >> 15;
+ amp += step;
+ }
+ }
+ }
+ if (mState == STARTING || mState == IDLE) {
+ return;
+ }
+ }
+
+ // do the actual processing in the effect engine
+ (*mEffectInterface)->process(mEffectInterface, &mConfig.inputCfg.buffer, &mConfig.outputCfg.buffer);
+
+ // clear auxiliary effect input buffer for next accumulation
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ memset(mConfig.inputCfg.buffer.raw, 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
+ }
+ } else if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT &&
+ mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw){
+ // If an insert effect is idle and input buffer is different from output buffer, copy input to
+ // output
+ sp<EffectChain> chain = mChain.promote();
+ if (chain != 0 && chain->activeTracks() != 0) {
+ size_t size = mConfig.inputCfg.buffer.frameCount * sizeof(int16_t);
+ if (mConfig.inputCfg.channels == CHANNEL_STEREO) {
+ size *= 2;
+ }
+ memcpy(mConfig.outputCfg.buffer.raw, mConfig.inputCfg.buffer.raw, size);
+ }
+ }
+}
+
+void AudioFlinger::EffectModule::reset()
+{
+ if (mEffectInterface == NULL) {
+ return;
+ }
+ (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_RESET, 0, NULL, 0, NULL);
+}
+
+status_t AudioFlinger::EffectModule::configure()
+{
+ uint32_t channels;
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ return DEAD_OBJECT;
+ }
+
+ // TODO: handle configuration of effects replacing track process
+ if (thread->channelCount() == 1) {
+ channels = CHANNEL_MONO;
+ } else {
+ channels = CHANNEL_STEREO;
+ }
+
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ mConfig.inputCfg.channels = CHANNEL_MONO;
+ } else {
+ mConfig.inputCfg.channels = channels;
+ }
+ mConfig.outputCfg.channels = channels;
+ mConfig.inputCfg.format = PCM_FORMAT_S15;
+ mConfig.outputCfg.format = PCM_FORMAT_S15;
+ mConfig.inputCfg.samplingRate = thread->sampleRate();
+ mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;
+ mConfig.inputCfg.bufferProvider.cookie = NULL;
+ mConfig.inputCfg.bufferProvider.getBuffer = NULL;
+ mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
+ mConfig.outputCfg.bufferProvider.cookie = NULL;
+ mConfig.outputCfg.bufferProvider.getBuffer = NULL;
+ mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
+ mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+ // Insert effect:
+ // - in session 0, always overwrites output buffer: input buffer == output buffer
+ // - in other sessions:
+ // last effect in the chain accumulates in output buffer: input buffer != output buffer
+ // other effect: overwrites output buffer: input buffer == output buffer
+ // Auxiliary effect:
+ // accumulates in output buffer: input buffer != output buffer
+ // Therefore: accumulate <=> input buffer != output buffer
+ if (mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) {
+ mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+ } else {
+ mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
+ }
+ mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
+ mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
+ mConfig.inputCfg.buffer.frameCount = thread->frameCount();
+ mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;
+
+ status_t cmdStatus;
+ int size = sizeof(int);
+ status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_CONFIGURE, sizeof(effect_config_t), &mConfig, &size, &cmdStatus);
+ if (status == 0) {
+ status = cmdStatus;
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::init()
+{
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ status_t cmdStatus;
+ int size = sizeof(status_t);
+ status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_INIT, 0, NULL, &size, &cmdStatus);
+ if (status == 0) {
+ status = cmdStatus;
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::start()
+{
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ status_t cmdStatus;
+ int size = sizeof(status_t);
+ status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_ENABLE, 0, NULL, &size, &cmdStatus);
+ if (status == 0) {
+ status = cmdStatus;
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::stop()
+{
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ status_t cmdStatus;
+ int size = sizeof(status_t);
+ status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_DISABLE, 0, NULL, &size, &cmdStatus);
+ if (status == 0) {
+ status = cmdStatus;
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData)
+{
+ LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface);
+
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ status_t status = (*mEffectInterface)->command(mEffectInterface, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) {
+ int size = (replySize == NULL) ? 0 : *replySize;
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 1; i < mHandles.size(); i++) {
+ sp<EffectHandle> h = mHandles[i].promote();
+ if (h != 0) {
+ h->commandExecuted(cmdCode, cmdSize, pCmdData, size, pReplyData);
+ }
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::setEnabled(bool enabled)
+{
+ Mutex::Autolock _l(mLock);
+ LOGV("setEnabled %p enabled %d", this, enabled);
+
+ if (enabled != isEnabled()) {
+ switch (mState) {
+ // going from disabled to enabled
+ case IDLE:
+ mState = RESET;
+ break;
+ case STOPPING:
+ mState = ACTIVE;
+ break;
+ case STOPPED:
+ mState = STARTING;
+ break;
+
+ // going from enabled to disabled
+ case RESET:
+ mState = IDLE;
+ break;
+ case STARTING:
+ mState = STOPPED;
+ break;
+ case ACTIVE:
+ mState = STOPPING;
+ break;
+ }
+ for (size_t i = 1; i < mHandles.size(); i++) {
+ sp<EffectHandle> h = mHandles[i].promote();
+ if (h != 0) {
+ h->setEnabled(enabled);
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+bool AudioFlinger::EffectModule::isEnabled()
+{
+ switch (mState) {
+ case RESET:
+ case STARTING:
+ case ACTIVE:
+ return true;
+ case IDLE:
+ case STOPPING:
+ case STOPPED:
+ default:
+ return false;
+ }
+}
+
+status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
+{
+ status_t status = NO_ERROR;
+
+ // Send volume indication if EFFECT_FLAG_VOLUME_IND is set and read back altered volume
+ // if controller flag is set (Note that controller == TRUE => EFFECT_FLAG_VOLUME_CTRL set)
+ if ((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) & (EFFECT_FLAG_VOLUME_CTRL|EFFECT_FLAG_VOLUME_IND)) {
+ status_t cmdStatus;
+ uint32_t volume[2];
+ uint32_t *pVolume = NULL;
+ int size = sizeof(volume);
+ volume[0] = *left;
+ volume[1] = *right;
+ if (controller) {
+ pVolume = volume;
+ }
+ status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_VOLUME, size, volume, &size, pVolume);
+ if (controller && status == NO_ERROR && size == sizeof(volume)) {
+ *left = volume[0];
+ *right = volume[1];
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
+{
+ status_t status = NO_ERROR;
+ if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_MASK) {
+ status_t cmdStatus;
+ int size = sizeof(status_t);
+ status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_DEVICE, sizeof(uint32_t), &device, &size, &cmdStatus);
+ if (status == NO_ERROR) {
+ status = cmdStatus;
+ }
+ }
+ return status;
+}
+
+
+status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "\tEffect ID %d:\n", mId);
+ result.append(buffer);
+
+ bool locked = tryLock(mLock);
+ // failed to lock - AudioFlinger is probably deadlocked
+ if (!locked) {
+ result.append("\t\tCould not lock Fx mutex:\n");
+ }
+
+ result.append("\t\tSession Status State Engine:\n");
+ snprintf(buffer, SIZE, "\t\t%05d %03d %03d 0x%08x\n",
+ mSessionId, mStatus, mState, (uint32_t)mEffectInterface);
+ result.append(buffer);
+
+ result.append("\t\tDescriptor:\n");
+ snprintf(buffer, SIZE, "\t\t- UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
+ mDescriptor.uuid.timeLow, mDescriptor.uuid.timeMid, mDescriptor.uuid.timeHiAndVersion,
+ mDescriptor.uuid.clockSeq, mDescriptor.uuid.node[0], mDescriptor.uuid.node[1],mDescriptor.uuid.node[2],
+ mDescriptor.uuid.node[3],mDescriptor.uuid.node[4],mDescriptor.uuid.node[5]);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\t- TYPE: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
+ mDescriptor.type.timeLow, mDescriptor.type.timeMid, mDescriptor.type.timeHiAndVersion,
+ mDescriptor.type.clockSeq, mDescriptor.type.node[0], mDescriptor.type.node[1],mDescriptor.type.node[2],
+ mDescriptor.type.node[3],mDescriptor.type.node[4],mDescriptor.type.node[5]);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\t- apiVersion: %04X\n\t\t- flags: %08X\n",
+ mDescriptor.apiVersion,
+ mDescriptor.flags);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\t- name: %s\n",
+ mDescriptor.name);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\t- implementor: %s\n",
+ mDescriptor.implementor);
+ result.append(buffer);
+
+ result.append("\t\t- Input configuration:\n");
+ result.append("\t\t\tBuffer Frames Smp rate Channels Format\n");
+ snprintf(buffer, SIZE, "\t\t\t0x%08x %05d %05d %08x %d\n",
+ (uint32_t)mConfig.inputCfg.buffer.raw,
+ mConfig.inputCfg.buffer.frameCount,
+ mConfig.inputCfg.samplingRate,
+ mConfig.inputCfg.channels,
+ mConfig.inputCfg.format);
+ result.append(buffer);
+
+ result.append("\t\t- Output configuration:\n");
+ result.append("\t\t\tBuffer Frames Smp rate Channels Format\n");
+ snprintf(buffer, SIZE, "\t\t\t0x%08x %05d %05d %08x %d\n",
+ (uint32_t)mConfig.outputCfg.buffer.raw,
+ mConfig.outputCfg.buffer.frameCount,
+ mConfig.outputCfg.samplingRate,
+ mConfig.outputCfg.channels,
+ mConfig.outputCfg.format);
+ result.append(buffer);
+
+ snprintf(buffer, SIZE, "\t\t%d Clients:\n", mHandles.size());
+ result.append(buffer);
+ result.append("\t\t\tPid Priority Ctrl Locked client server\n");
+ for (size_t i = 0; i < mHandles.size(); ++i) {
+ sp<EffectHandle> handle = mHandles[i].promote();
+ if (handle != 0) {
+ handle->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ }
+
+ result.append("\n");
+
+ write(fd, result.string(), result.length());
+
+ if (locked) {
+ mLock.unlock();
+ }
+
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+// EffectHandle implementation
+// ----------------------------------------------------------------------------
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectHandle"
+
+AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect,
+ const sp<AudioFlinger::Client>& client,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority)
+ : BnEffect(),
+ mEffect(effect), mEffectClient(effectClient), mClient(client), mPriority(priority), mHasControl(false)
+{
+ LOGV("constructor %p", this);
+
+ int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
+ mCblkMemory = client->heap()->allocate(EFFECT_PARAM_BUFFER_SIZE + bufOffset);
+ if (mCblkMemory != 0) {
+ mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer());
+
+ if (mCblk) {
+ new(mCblk) effect_param_cblk_t();
+ mBuffer = (uint8_t *)mCblk + bufOffset;
+ }
+ } else {
+ LOGE("not enough memory for Effect size=%u", EFFECT_PARAM_BUFFER_SIZE + sizeof(effect_param_cblk_t));
+ return;
+ }
+}
+
+AudioFlinger::EffectHandle::~EffectHandle()
+{
+ LOGV("Destructor %p", this);
+ disconnect();
+}
+
+status_t AudioFlinger::EffectHandle::enable()
+{
+ if (!mHasControl) return INVALID_OPERATION;
+ if (mEffect == 0) return DEAD_OBJECT;
+
+ return mEffect->setEnabled(true);
+}
+
+status_t AudioFlinger::EffectHandle::disable()
+{
+ if (!mHasControl) return INVALID_OPERATION;
+ if (mEffect == NULL) return DEAD_OBJECT;
+
+ return mEffect->setEnabled(false);
+}
+
+void AudioFlinger::EffectHandle::disconnect()
+{
+ if (mEffect == 0) {
+ return;
+ }
+ mEffect->disconnect(this);
+ // release sp on module => module destructor can be called now
+ mEffect.clear();
+ if (mCblk) {
+ mCblk->~effect_param_cblk_t(); // destroy our shared-structure.
+ }
+ mCblkMemory.clear(); // and free the shared memory
+ if (mClient != 0) {
+ Mutex::Autolock _l(mClient->audioFlinger()->mLock);
+ mClient.clear();
+ }
+}
+
+status_t AudioFlinger::EffectHandle::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData)
+{
+ LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p", cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get());
+
+ // only get parameter command is permitted for applications not controlling the effect
+ if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) {
+ return INVALID_OPERATION;
+ }
+ if (mEffect == 0) return DEAD_OBJECT;
+
+ // handle commands that are not forwarded transparently to effect engine
+ if (cmdCode == EFFECT_CMD_SET_PARAM_COMMIT) {
+ // No need to trylock() here as this function is executed in the binder thread serving a particular client process:
+ // no risk to block the whole media server process or mixer threads is we are stuck here
+ Mutex::Autolock _l(mCblk->lock);
+ if (mCblk->clientIndex > EFFECT_PARAM_BUFFER_SIZE ||
+ mCblk->serverIndex > EFFECT_PARAM_BUFFER_SIZE) {
+ mCblk->serverIndex = 0;
+ mCblk->clientIndex = 0;
+ return BAD_VALUE;
+ }
+ status_t status = NO_ERROR;
+ while (mCblk->serverIndex < mCblk->clientIndex) {
+ int reply;
+ int rsize = sizeof(int);
+ int *p = (int *)(mBuffer + mCblk->serverIndex);
+ int size = *p++;
+ effect_param_t *param = (effect_param_t *)p;
+ int psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
+ status_t ret = mEffect->command(EFFECT_CMD_SET_PARAM, psize, p, &rsize, &reply);
+ if (ret == NO_ERROR) {
+ if (reply != NO_ERROR) {
+ status = reply;
+ }
+ } else {
+ status = ret;
+ }
+ mCblk->serverIndex += size;
+ }
+ mCblk->serverIndex = 0;
+ mCblk->clientIndex = 0;
+ return status;
+ } else if (cmdCode == EFFECT_CMD_ENABLE) {
+ return enable();
+ } else if (cmdCode == EFFECT_CMD_DISABLE) {
+ return disable();
+ }
+
+ return mEffect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+}
+
+sp<IMemory> AudioFlinger::EffectHandle::getCblk() const {
+ return mCblkMemory;
+}
+
+void AudioFlinger::EffectHandle::setControl(bool hasControl, bool signal)
+{
+ LOGV("setControl %p control %d", this, hasControl);
+
+ mHasControl = hasControl;
+ if (signal && mEffectClient != 0) {
+ mEffectClient->controlStatusChanged(hasControl);
+ }
+}
+
+void AudioFlinger::EffectHandle::commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData)
+{
+ if (mEffectClient != 0) {
+ mEffectClient->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ }
+}
+
+
+
+void AudioFlinger::EffectHandle::setEnabled(bool enabled)
+{
+ if (mEffectClient != 0) {
+ mEffectClient->enableStatusChanged(enabled);
+ }
+}
+
+status_t AudioFlinger::EffectHandle::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ return BnEffect::onTransact(code, data, reply, flags);
+}
+
+
+void AudioFlinger::EffectHandle::dump(char* buffer, size_t size)
+{
+ bool locked = tryLock(mCblk->lock);
+
+ snprintf(buffer, size, "\t\t\t%05d %05d %01u %01u %05u %05u\n",
+ (mClient == NULL) ? getpid() : mClient->pid(),
+ mPriority,
+ mHasControl,
+ !locked,
+ mCblk->clientIndex,
+ mCblk->serverIndex
+ );
+
+ if (locked) {
+ mCblk->lock.unlock();
+ }
+}
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectChain"
+
+AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread,
+ int sessionId)
+ : mThread(wThread), mSessionId(sessionId), mVolumeCtrlIdx(-1), mActiveTrackCnt(0), mOwnInBuffer(false)
+{
+
+}
+
+AudioFlinger::EffectChain::~EffectChain()
+{
+ if (mOwnInBuffer) {
+ delete mInBuffer;
+ }
+
+}
+
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromDesc(effect_descriptor_t *descriptor)
+{
+ sp<EffectModule> effect;
+ size_t size = mEffects.size();
+
+ for (size_t i = 0; i < size; i++) {
+ if (memcmp(&mEffects[i]->desc().uuid, &descriptor->uuid, sizeof(effect_uuid_t)) == 0) {
+ effect = mEffects[i];
+ break;
+ }
+ }
+ return effect;
+}
+
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromId(int id)
+{
+ sp<EffectModule> effect;
+ size_t size = mEffects.size();
+
+ for (size_t i = 0; i < size; i++) {
+ if (mEffects[i]->id() == id) {
+ effect = mEffects[i];
+ break;
+ }
+ }
+ return effect;
+}
+
+// Must be called with EffectChain::mLock locked
+void AudioFlinger::EffectChain::process_l()
+{
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->process();
+ }
+ // if no track is active, input buffer must be cleared here as the mixer process
+ // will not do it
+ if (mSessionId != 0 && activeTracks() == 0) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ size_t numSamples = thread->frameCount() * thread->channelCount();
+ memset(mInBuffer, 0, numSamples * sizeof(int16_t));
+ }
+ }
+}
+
+status_t AudioFlinger::EffectChain::addEffect(sp<EffectModule>& effect)
+{
+ effect_descriptor_t desc = effect->desc();
+ uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;
+
+ Mutex::Autolock _l(mLock);
+
+ if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ // Auxiliary effects are inserted at the beginning of mEffects vector as
+ // they are processed first and accumulated in chain input buffer
+ mEffects.insertAt(effect, 0);
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ return NO_INIT;
+ }
+ // the input buffer for auxiliary effect contains mono samples in
+ // 32 bit format. This is to avoid saturation in AudoMixer
+ // accumulation stage. Saturation is done in EffectModule::process() before
+ // calling the process in effect engine
+ size_t numSamples = thread->frameCount();
+ int32_t *buffer = new int32_t[numSamples];
+ memset(buffer, 0, numSamples * sizeof(int32_t));
+ effect->setInBuffer((int16_t *)buffer);
+ // auxiliary effects output samples to chain input buffer for further processing
+ // by insert effects
+ effect->setOutBuffer(mInBuffer);
+ } else {
+ // Insert effects are inserted at the end of mEffects vector as they are processed
+ // after track and auxiliary effects.
+ // Insert effect order:
+ // if EFFECT_FLAG_INSERT_FIRST or EFFECT_FLAG_INSERT_EXCLUSIVE insert as first insert effect
+ // else if EFFECT_FLAG_INSERT_ANY insert after first or before last
+ // else insert as last insert effect
+ // Reject insertion if:
+ // - EFFECT_FLAG_INSERT_EXCLUSIVE and another effect is present
+ // - an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is present
+ // - EFFECT_FLAG_INSERT_FIRST or EFFECT_FLAG_INSERT_LAST and an effect with same
+ // preference is present
+
+ int size = (int)mEffects.size();
+ int idx_insert = size;
+ int idx_insert_first = -1;
+ int idx_insert_last = -1;
+
+ for (int i = 0; i < size; i++) {
+ effect_descriptor_t d = mEffects[i]->desc();
+ uint32_t iMode = d.flags & EFFECT_FLAG_TYPE_MASK;
+ uint32_t iPref = d.flags & EFFECT_FLAG_INSERT_MASK;
+ if (iMode == EFFECT_FLAG_TYPE_INSERT) {
+ // check invalid effect chaining combinations
+ if (insertPref == EFFECT_FLAG_INSERT_EXCLUSIVE ||
+ iPref == EFFECT_FLAG_INSERT_EXCLUSIVE ||
+ (insertPref != EFFECT_FLAG_INSERT_ANY
+ && insertPref == iPref)) {
+ return INVALID_OPERATION;
+ }
+ // remember position of first insert effect
+ if (idx_insert == size) {
+ idx_insert = i;
+ }
+ // remember position of insert effect claiming
+ // first place
+ if (iPref == EFFECT_FLAG_INSERT_FIRST) {
+ idx_insert_first = i;
+ }
+ // remember position of insert effect claiming
+ // last place
+ if (iPref == EFFECT_FLAG_INSERT_LAST) {
+ idx_insert_last = i;
+ }
+ }
+ }
+
+ // modify idx_insert from first place if needed
+ if (idx_insert_first != -1) {
+ idx_insert = idx_insert_first + 1;
+ } else if (idx_insert_last != -1) {
+ idx_insert = idx_insert_last;
+ } else if (insertPref == EFFECT_FLAG_INSERT_LAST) {
+ idx_insert = size;
+ }
+
+ // always read samples from chain input buffer
+ effect->setInBuffer(mInBuffer);
+
+ // if last effect in the chain, output samples to chain
+ // output buffer, otherwise to chain input buffer
+ if (idx_insert == size) {
+ if (idx_insert != 0) {
+ mEffects[idx_insert-1]->setOutBuffer(mInBuffer);
+ mEffects[idx_insert-1]->configure();
+ }
+ effect->setOutBuffer(mOutBuffer);
+ } else {
+ effect->setOutBuffer(mInBuffer);
+ }
+ status_t status = mEffects.insertAt(effect, idx_insert);
+ // Always give volume control to last effect in chain with volume control capability
+ if (((desc.flags & EFFECT_FLAG_VOLUME_MASK) & EFFECT_FLAG_VOLUME_CTRL) &&
+ mVolumeCtrlIdx < idx_insert) {
+ mVolumeCtrlIdx = idx_insert;
+ }
+
+ LOGV("addEffect() effect %p, added in chain %p at rank %d status %d", effect.get(), this, idx_insert, status);
+ }
+ effect->configure();
+ return NO_ERROR;
+}
+
+size_t AudioFlinger::EffectChain::removeEffect(const sp<EffectModule>& effect)
+{
+ Mutex::Autolock _l(mLock);
+
+ int size = (int)mEffects.size();
+ int i;
+ uint32_t type = effect->desc().flags & EFFECT_FLAG_TYPE_MASK;
+
+ for (i = 0; i < size; i++) {
+ if (effect == mEffects[i]) {
+ if (type == EFFECT_FLAG_TYPE_AUXILIARY) {
+ delete[] effect->inBuffer();
+ } else {
+ if (i == size - 1 && i != 0) {
+ mEffects[i - 1]->setOutBuffer(mOutBuffer);
+ mEffects[i - 1]->configure();
+ }
+ }
+ mEffects.removeAt(i);
+ LOGV("removeEffect() effect %p, removed from chain %p at rank %d", effect.get(), this, i);
+ break;
+ }
+ }
+ // Return volume control to last effect in chain with volume control capability
+ if (mVolumeCtrlIdx == i) {
+ size = (int)mEffects.size();
+ for (i = size; i > 0; i--) {
+ if ((mEffects[i - 1]->desc().flags & EFFECT_FLAG_VOLUME_MASK) & EFFECT_FLAG_VOLUME_CTRL) {
+ break;
+ }
+ }
+ // mVolumeCtrlIdx reset to -1 if no effect found with volume control flag set
+ mVolumeCtrlIdx = i - 1;
+ }
+
+ return mEffects.size();
+}
+
+void AudioFlinger::EffectChain::setDevice(uint32_t device)
+{
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->setDevice(device);
+ }
+}
+
+bool AudioFlinger::EffectChain::setVolume(uint32_t *left, uint32_t *right)
+{
+ uint32_t newLeft = *left;
+ uint32_t newRight = *right;
+ bool hasControl = false;
+
+ // first get volume update from volume controller
+ if (mVolumeCtrlIdx >= 0) {
+ mEffects[mVolumeCtrlIdx]->setVolume(&newLeft, &newRight, true);
+ hasControl = true;
+ }
+ // then indicate volume to all other effects in chain.
+ // Pass altered volume to effects before volume controller
+ // and requested volume to effects after controller
+ uint32_t lVol = newLeft;
+ uint32_t rVol = newRight;
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ if ((int)i == mVolumeCtrlIdx) continue;
+ // this also works for mVolumeCtrlIdx == -1 when there is no volume controller
+ if ((int)i > mVolumeCtrlIdx) {
+ lVol = *left;
+ rVol = *right;
+ }
+ mEffects[i]->setVolume(&lVol, &rVol, false);
+ }
+ *left = newLeft;
+ *right = newRight;
+
+ return hasControl;
+}
+
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getVolumeController()
+{
+ sp<EffectModule> effect;
+ if (mVolumeCtrlIdx >= 0) {
+ effect = mEffects[mVolumeCtrlIdx];
+ }
+ return effect;
+}
+
+
+status_t AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "Effects for session %d:\n", mSessionId);
+ result.append(buffer);
+
+ bool locked = tryLock(mLock);
+ // failed to lock - AudioFlinger is probably deadlocked
+ if (!locked) {
+ result.append("\tCould not lock mutex:\n");
+ }
+
+ result.append("\tNum fx In buffer Out buffer Vol ctrl Active tracks:\n");
+ snprintf(buffer, SIZE, "\t%02d 0x%08x 0x%08x %02d %d\n",
+ mEffects.size(),
+ (uint32_t)mInBuffer,
+ (uint32_t)mOutBuffer,
+ (mVolumeCtrlIdx == -1) ? 0 : mEffects[mVolumeCtrlIdx]->id(),
+ mActiveTrackCnt);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ for (size_t i = 0; i < mEffects.size(); ++i) {
+ sp<EffectModule> effect = mEffects[i];
+ if (effect != 0) {
+ effect->dump(fd, args);
+ }
+ }
+
+ if (locked) {
+ mLock.unlock();
+ }
+
+ return NO_ERROR;
+}
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger"
+
// ----------------------------------------------------------------------------
status_t AudioFlinger::onTransact(
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index f35f38b..e543334 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -42,6 +42,7 @@
namespace android {
class audio_track_cblk_t;
+class effect_param_cblk_t;
class AudioMixer;
class AudioBuffer;
class AudioResampler;
@@ -75,6 +76,7 @@
uint32_t flags,
const sp<IMemory>& sharedBuffer,
int output,
+ int *sessionId,
status_t *status);
virtual uint32_t sampleRate(int output) const;
@@ -139,6 +141,28 @@
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output);
+ virtual int newAudioSessionId();
+
+ virtual status_t loadEffectLibrary(const char *libPath, int *handle);
+
+ virtual status_t unloadEffectLibrary(int handle);
+
+ virtual status_t queryNumberEffects(uint32_t *numEffects);
+
+ virtual status_t queryNextEffect(effect_descriptor_t *descriptor);
+
+ virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor);
+
+ virtual sp<IEffect> createEffect(pid_t pid,
+ effect_descriptor_t *pDesc,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority,
+ int output,
+ int sessionId,
+ status_t *status,
+ int *id,
+ int *enabled);
+
enum hardware_call_state {
AUDIO_HW_IDLE = 0,
AUDIO_HW_INIT,
@@ -167,6 +191,7 @@
int channelCount,
int frameCount,
uint32_t flags,
+ int *sessionId,
status_t *status);
virtual status_t onTransact(
@@ -233,6 +258,9 @@
class DuplicatingThread;
class Track;
class RecordTrack;
+ class EffectModule;
+ class EffectHandle;
+ class EffectChain;
class ThreadBase : public Thread {
public:
@@ -268,13 +296,15 @@
int channelCount,
int frameCount,
uint32_t flags,
- const sp<IMemory>& sharedBuffer);
+ const sp<IMemory>& sharedBuffer,
+ int sessionId);
~TrackBase();
virtual status_t start() = 0;
virtual void stop() = 0;
sp<IMemory> getCblk() const;
audio_track_cblk_t* cblk() const { return mCblk; }
+ int sessionId() { return mSessionId; }
protected:
friend class ThreadBase;
@@ -323,6 +353,7 @@
int mClientTid;
uint8_t mFormat;
uint32_t mFlags;
+ int mSessionId;
};
class ConfigEvent {
@@ -405,7 +436,8 @@
int format,
int channelCount,
int frameCount,
- const sp<IMemory>& sharedBuffer);
+ const sp<IMemory>& sharedBuffer,
+ int sessionId);
~Track();
void dump(char* buffer, size_t size);
@@ -424,6 +456,12 @@
int type() const {
return mStreamType;
}
+ status_t attachAuxEffect(int EffectId);
+ void setAuxBuffer(int EffectId, int32_t *buffer);
+ int32_t *auxBuffer() { return mAuxBuffer; }
+ void setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }
+ int16_t *mainBuffer() { return mMainBuffer; }
+ int auxEffectId() { return mAuxEffectId; }
protected:
@@ -464,6 +502,9 @@
bool mResetDone;
int mStreamType;
int mName;
+ int16_t *mMainBuffer;
+ int32_t *mAuxBuffer;
+ int mAuxEffectId;
}; // end of Track
@@ -505,7 +546,7 @@
DuplicatingThread* mSourceThread;
}; // end of OutputTrack
- PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+ PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
virtual ~PlaybackThread();
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -538,6 +579,7 @@
int channelCount,
int frameCount,
const sp<IMemory>& sharedBuffer,
+ int sessionId,
status_t *status);
AudioStreamOut* getOutput() { return mOutput; }
@@ -549,6 +591,29 @@
virtual String8 getParameters(const String8& keys);
virtual void audioConfigChanged_l(int event, int param = 0);
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
+ int16_t *mixBuffer() { return mMixBuffer; };
+
+ sp<EffectHandle> createEffect_l(
+ const sp<AudioFlinger::Client>& client,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority,
+ int sessionId,
+ effect_descriptor_t *desc,
+ int *enabled,
+ status_t *status);
+
+ bool hasAudioSession(int sessionId);
+ sp<EffectChain> getEffectChain(int sessionId);
+ sp<EffectChain> getEffectChain_l(int sessionId);
+ status_t addEffectChain_l(const sp<EffectChain>& chain);
+ size_t removeEffectChain_l(const sp<EffectChain>& chain);
+ void lockEffectChains_l();
+ void unlockEffectChains();
+
+ sp<AudioFlinger::EffectModule> getEffect_l(int sessionId, int effectId);
+ void detachAuxEffect_l(int effectId);
+ status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId);
+ status_t attachAuxEffect_l(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId);
struct stream_type_t {
stream_type_t()
@@ -591,8 +656,11 @@
void readOutputParameters();
+ uint32_t device() { return mDevice; }
+
virtual status_t dumpInternals(int fd, const Vector<String16>& args);
status_t dumpTracks(int fd, const Vector<String16>& args);
+ status_t dumpEffectChains(int fd, const Vector<String16>& args);
SortedVector< sp<Track> > mTracks;
// mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread
@@ -603,11 +671,13 @@
int mNumWrites;
int mNumDelayedWrites;
bool mInWrite;
+ Vector< sp<EffectChain> > mEffectChains;
+ uint32_t mDevice;
};
class MixerThread : public PlaybackThread {
public:
- MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+ MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
virtual ~MixerThread();
// Thread virtuals
@@ -630,7 +700,7 @@
class DirectOutputThread : public PlaybackThread {
public:
- DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+ DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
~DirectOutputThread();
// Thread virtuals
@@ -645,8 +715,12 @@
virtual uint32_t idleSleepTimeUs();
private:
- float mLeftVolume;
- float mRightVolume;
+ void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
+
+ float mLeftVolFloat;
+ float mRightVolFloat;
+ uint16_t mLeftVolShort;
+ uint16_t mRightVolShort;
};
class DuplicatingThread : public MixerThread {
@@ -676,6 +750,8 @@
float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; }
void audioConfigChanged_l(int event, int ioHandle, void *param2);
+ int nextUniqueId();
+
friend class AudioBuffer;
class TrackHandle : public android::BnAudioTrack {
@@ -689,6 +765,7 @@
virtual void pause();
virtual void setVolume(float left, float right);
virtual sp<IMemory> getCblk() const;
+ virtual status_t attachAuxEffect(int effectId);
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
private:
@@ -717,7 +794,8 @@
int format,
int channelCount,
int frameCount,
- uint32_t flags);
+ uint32_t flags,
+ int sessionId);
~RecordTrack();
virtual status_t start();
@@ -792,6 +870,215 @@
sp<RecordThread::RecordTrack> mRecordTrack;
};
+ //--- Audio Effect Management
+
+ // EffectModule and EffectChain classes both have their own mutex to protect
+ // state changes or resource modifications. Always respect the following order
+ // if multiple mutexes must be acquired to avoid cross deadlock:
+ // AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
+
+ // The EffectModule class is a wrapper object controlling the effect engine implementation
+ // in the effect library. It prevents concurrent calls to process() and command() functions
+ // from different client threads. It keeps a list of EffectHandle objects corresponding
+ // to all client applications using this effect and notifies applications of effect state,
+ // control or parameter changes. It manages the activation state machine to send appropriate
+ // reset, enable, disable commands to effect engine and provide volume
+ // ramping when effects are activated/deactivated.
+ // When controlling an auxiliary effect, the EffectModule also provides an input buffer used by
+ // the attached track(s) to accumulate their auxiliary channel.
+ class EffectModule: public RefBase {
+ public:
+ EffectModule(const wp<ThreadBase>& wThread,
+ const wp<AudioFlinger::EffectChain>& chain,
+ effect_descriptor_t *desc,
+ int id,
+ int sessionId);
+ ~EffectModule();
+
+ enum effect_state {
+ IDLE,
+ RESET,
+ STARTING,
+ ACTIVE,
+ STOPPING,
+ STOPPED
+ };
+
+ int id() { return mId; }
+ void process();
+ status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
+
+ void reset();
+ status_t configure();
+ status_t init();
+ uint32_t state() {
+ return mState;
+ }
+ uint32_t status() {
+ return mStatus;
+ }
+ status_t setEnabled(bool enabled);
+ bool isEnabled();
+
+ void setInBuffer(int16_t *buffer) { mConfig.inputCfg.buffer.s16 = buffer; }
+ int16_t *inBuffer() { return mConfig.inputCfg.buffer.s16; }
+ void setOutBuffer(int16_t *buffer) { mConfig.outputCfg.buffer.s16 = buffer; }
+ int16_t *outBuffer() { return mConfig.outputCfg.buffer.s16; }
+
+ status_t addHandle(sp<EffectHandle>& handle);
+ void disconnect(const wp<EffectHandle>& handle);
+ size_t removeHandle (const wp<EffectHandle>& handle);
+
+ effect_descriptor_t& desc() { return mDescriptor; }
+
+ status_t setDevice(uint32_t device);
+ status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
+
+ status_t dump(int fd, const Vector<String16>& args);
+
+ protected:
+
+ EffectModule(const EffectModule&);
+ EffectModule& operator = (const EffectModule&);
+
+ status_t start();
+ status_t stop();
+
+ Mutex mLock; // mutex for process, commands and handles list protection
+ wp<ThreadBase> mThread; // parent thread
+ wp<EffectChain> mChain; // parent effect chain
+ int mId; // this instance unique ID
+ int mSessionId; // audio session ID
+ effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
+ effect_config_t mConfig; // input and output audio configuration
+ effect_interface_t mEffectInterface; // Effect module C API
+ status_t mStatus; // initialization status
+ uint32_t mState; // current activation state (effect_state)
+ Vector< wp<EffectHandle> > mHandles; // list of client handles
+ };
+
+ // The EffectHandle class implements the IEffect interface. It provides resources
+ // to receive parameter updates, keeps track of effect control
+ // ownership and state and has a pointer to the EffectModule object it is controlling.
+ // There is one EffectHandle object for each application controlling (or using)
+ // an effect module.
+ // The EffectHandle is obtained by calling AudioFlinger::createEffect().
+ class EffectHandle: public android::BnEffect {
+ public:
+
+ EffectHandle(const sp<EffectModule>& effect,
+ const sp<AudioFlinger::Client>& client,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority);
+ virtual ~EffectHandle();
+
+ // IEffect
+ virtual status_t enable();
+ virtual status_t disable();
+ virtual status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
+ virtual void disconnect();
+ virtual sp<IMemory> getCblk() const;
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+
+
+ // Give or take control of effect module
+ void setControl(bool hasControl, bool signal);
+ void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData);
+ void setEnabled(bool enabled);
+
+ // Getters
+ int id() { return mEffect->id(); }
+ int priority() { return mPriority; }
+ bool hasControl() { return mHasControl; }
+ sp<EffectModule> effect() { return mEffect; }
+
+ void dump(char* buffer, size_t size);
+
+ protected:
+
+ EffectHandle(const EffectHandle&);
+ EffectHandle& operator =(const EffectHandle&);
+
+ sp<EffectModule> mEffect; // pointer to controlled EffectModule
+ sp<IEffectClient> mEffectClient; // callback interface for client notifications
+ sp<Client> mClient; // client for shared memory allocation
+ sp<IMemory> mCblkMemory; // shared memory for control block
+ effect_param_cblk_t* mCblk; // control block for deferred parameter setting via shared memory
+ uint8_t* mBuffer; // pointer to parameter area in shared memory
+ int mPriority; // client application priority to control the effect
+ bool mHasControl; // true if this handle is controlling the effect
+ };
+
+ // the EffectChain class represents a group of effects associated to one audio session.
+ // There can be any number of EffectChain objects per output mixer thread (PlaybackThread).
+ // The EffecChain with session ID 0 contains global effects applied to the output mix.
+ // Effects in this chain can be insert or auxiliary. Effects in other chains (attached to tracks)
+ // are insert only. The EffectChain maintains an ordered list of effect module, the order corresponding
+ // in the effect process order. When attached to a track (session ID != 0), it also provide it's own
+ // input buffer used by the track as accumulation buffer.
+ class EffectChain: public RefBase {
+ public:
+ EffectChain(const wp<ThreadBase>& wThread, int sessionId);
+ ~EffectChain();
+
+ void process_l();
+
+ void lock() {
+ mLock.lock();
+ }
+ void unlock() {
+ mLock.unlock();
+ }
+
+ status_t addEffect(sp<EffectModule>& handle);
+ size_t removeEffect(const sp<EffectModule>& handle);
+
+ int sessionId() {
+ return mSessionId;
+ }
+ sp<EffectModule> getEffectFromDesc(effect_descriptor_t *descriptor);
+ sp<EffectModule> getEffectFromId(int id);
+ sp<EffectModule> getVolumeController();
+ bool setVolume(uint32_t *left, uint32_t *right);
+ void setDevice(uint32_t device);
+
+ void setInBuffer(int16_t *buffer, bool ownsBuffer = false) {
+ mInBuffer = buffer;
+ mOwnInBuffer = ownsBuffer;
+ }
+ int16_t *inBuffer() {
+ return mInBuffer;
+ }
+ void setOutBuffer(int16_t *buffer) {
+ mOutBuffer = buffer;
+ }
+ int16_t *outBuffer() {
+ return mOutBuffer;
+ }
+
+ void startTrack() {mActiveTrackCnt++;}
+ void stopTrack() {mActiveTrackCnt--;}
+ int activeTracks() { return mActiveTrackCnt;}
+
+ status_t dump(int fd, const Vector<String16>& args);
+
+ protected:
+
+ EffectChain(const EffectChain&);
+ EffectChain& operator =(const EffectChain&);
+
+ wp<ThreadBase> mThread; // parent mixer thread
+ Mutex mLock; // mutex protecting effect list
+ Vector<sp<EffectModule> > mEffects; // list of effect modules
+ int mSessionId; // audio session ID
+ int16_t *mInBuffer; // chain input buffer
+ int16_t *mOutBuffer; // chain output buffer
+ int mVolumeCtrlIdx; // index of insert effect having control over volume
+ int mActiveTrackCnt; // number of active tracks connected
+ bool mOwnInBuffer; // true if the chain owns its input buffer
+ };
+
friend class RecordThread;
friend class PlaybackThread;
@@ -813,7 +1100,7 @@
DefaultKeyedVector< int, sp<RecordThread> > mRecordThreads;
DefaultKeyedVector< pid_t, sp<NotificationClient> > mNotificationClients;
- int mNextThreadId;
+ volatile int32_t mNextUniqueId;
#ifdef LVMX
int mLifeVibesClientPid;
#endif
diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp
index 19a442a..8aaa325 100644
--- a/libs/audioflinger/AudioMixer.cpp
+++ b/libs/audioflinger/AudioMixer.cpp
@@ -56,6 +56,8 @@
t->volume[1] = UNITY_GAIN;
t->volumeInc[0] = 0;
t->volumeInc[1] = 0;
+ t->auxLevel = 0;
+ t->auxInc = 0;
t->channelCount = 2;
t->enabled = 0;
t->format = 16;
@@ -65,6 +67,8 @@
t->resampler = 0;
t->sampleRate = mSampleRate;
t->in = 0;
+ t->mainBuffer = NULL;
+ t->auxBuffer = NULL;
t++;
}
}
@@ -169,28 +173,48 @@
return NO_ERROR;
}
-status_t AudioMixer::setParameter(int target, int name, int value)
+status_t AudioMixer::setParameter(int target, int name, void *value)
{
+ int valueInt = (int)value;
+ int32_t *valueBuf = (int32_t *)value;
+
switch (target) {
case TRACK:
if (name == CHANNEL_COUNT) {
- if ((uint32_t(value) <= MAX_NUM_CHANNELS) && (value)) {
- if (mState.tracks[ mActiveTrack ].channelCount != value) {
- mState.tracks[ mActiveTrack ].channelCount = value;
- LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", value);
+ if ((uint32_t(valueInt) <= MAX_NUM_CHANNELS) && (valueInt)) {
+ if (mState.tracks[ mActiveTrack ].channelCount != valueInt) {
+ mState.tracks[ mActiveTrack ].channelCount = valueInt;
+ LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", valueInt);
invalidateState(1<<mActiveTrack);
}
return NO_ERROR;
}
}
+ if (name == MAIN_BUFFER) {
+ if (mState.tracks[ mActiveTrack ].mainBuffer != valueBuf) {
+ mState.tracks[ mActiveTrack ].mainBuffer = valueBuf;
+ LOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
+ invalidateState(1<<mActiveTrack);
+ }
+ return NO_ERROR;
+ }
+ if (name == AUX_BUFFER) {
+ if (mState.tracks[ mActiveTrack ].auxBuffer != valueBuf) {
+ mState.tracks[ mActiveTrack ].auxBuffer = valueBuf;
+ LOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf);
+ invalidateState(1<<mActiveTrack);
+ }
+ return NO_ERROR;
+ }
+
break;
case RESAMPLE:
if (name == SAMPLE_RATE) {
- if (value > 0) {
+ if (valueInt > 0) {
track_t& track = mState.tracks[ mActiveTrack ];
- if (track.setResampler(uint32_t(value), mSampleRate)) {
+ if (track.setResampler(uint32_t(valueInt), mSampleRate)) {
LOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)",
- uint32_t(value));
+ uint32_t(valueInt));
invalidateState(1<<mActiveTrack);
}
return NO_ERROR;
@@ -201,18 +225,39 @@
case VOLUME:
if ((uint32_t(name-VOLUME0) < MAX_NUM_CHANNELS)) {
track_t& track = mState.tracks[ mActiveTrack ];
- if (track.volume[name-VOLUME0] != value) {
+ if (track.volume[name-VOLUME0] != valueInt) {
+ LOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt);
track.prevVolume[name-VOLUME0] = track.volume[name-VOLUME0] << 16;
- track.volume[name-VOLUME0] = value;
+ track.volume[name-VOLUME0] = valueInt;
if (target == VOLUME) {
- track.prevVolume[name-VOLUME0] = value << 16;
+ track.prevVolume[name-VOLUME0] = valueInt << 16;
track.volumeInc[name-VOLUME0] = 0;
} else {
- int32_t d = (value<<16) - track.prevVolume[name-VOLUME0];
+ int32_t d = (valueInt<<16) - track.prevVolume[name-VOLUME0];
int32_t volInc = d / int32_t(mState.frameCount);
track.volumeInc[name-VOLUME0] = volInc;
if (volInc == 0) {
- track.prevVolume[name-VOLUME0] = value << 16;
+ track.prevVolume[name-VOLUME0] = valueInt << 16;
+ }
+ }
+ invalidateState(1<<mActiveTrack);
+ }
+ return NO_ERROR;
+ } else if (name == AUXLEVEL) {
+ track_t& track = mState.tracks[ mActiveTrack ];
+ if (track.auxLevel != valueInt) {
+ LOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt);
+ track.prevAuxLevel = track.auxLevel << 16;
+ track.auxLevel = valueInt;
+ if (target == VOLUME) {
+ track.prevAuxLevel = valueInt << 16;
+ track.auxInc = 0;
+ } else {
+ int32_t d = (valueInt<<16) - track.prevAuxLevel;
+ int32_t volInc = d / int32_t(mState.frameCount);
+ track.auxInc = volInc;
+ if (volInc == 0) {
+ track.prevAuxLevel = valueInt << 16;
}
}
invalidateState(1<<mActiveTrack);
@@ -245,7 +290,7 @@
}
inline
-void AudioMixer::track_t::adjustVolumeRamp()
+void AudioMixer::track_t::adjustVolumeRamp(bool aux)
{
for (int i=0 ; i<2 ; i++) {
if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) ||
@@ -254,6 +299,13 @@
prevVolume[i] = volume[i]<<16;
}
}
+ if (aux) {
+ if (((auxInc>0) && (((prevAuxLevel+auxInc)>>16) >= auxLevel)) ||
+ ((auxInc<0) && (((prevAuxLevel+auxInc)>>16) <= auxLevel))) {
+ auxInc = 0;
+ prevAuxLevel = auxLevel<<16;
+ }
+ }
}
@@ -265,13 +317,13 @@
-void AudioMixer::process(void* output)
+void AudioMixer::process()
{
- mState.hook(&mState, output);
+ mState.hook(&mState);
}
-void AudioMixer::process__validate(state_t* state, void* output)
+void AudioMixer::process__validate(state_t* state)
{
LOGW_IF(!state->needsChanged,
"in process__validate() but nothing's invalid");
@@ -308,7 +360,10 @@
n |= NEEDS_CHANNEL_1 + t.channelCount - 1;
n |= NEEDS_FORMAT_16;
n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED;
-
+ if (t.auxLevel != 0 && t.auxBuffer != NULL) {
+ n |= NEEDS_AUX_ENABLED;
+ }
+
if (t.volumeInc[0]|t.volumeInc[1]) {
volumeRamp = 1;
} else if (!t.doesResample() && t.volumeRL == 0) {
@@ -319,6 +374,9 @@
if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) {
t.hook = track__nop;
} else {
+ if ((n & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+ all16BitsStereoNoResample = 0;
+ }
if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
all16BitsStereoNoResample = 0;
resampling = 1;
@@ -369,7 +427,7 @@
countActiveTracks, state->enabledTracks,
all16BitsStereoNoResample, resampling, volumeRamp);
- state->hook(state, output);
+ state->hook(state);
// Now that the volume ramp has been done, set optimal state and
// track hooks for subsequent mixer process
@@ -390,7 +448,7 @@
}
if (allMuted) {
state->hook = process__nop;
- } else if (!resampling && all16BitsStereoNoResample) {
+ } else if (all16BitsStereoNoResample) {
if (countActiveTracks == 1) {
state->hook = process__OneTrack16BitsStereoNoResampling;
}
@@ -481,30 +539,44 @@
}
-void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp)
+void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
{
t->resampler->setSampleRate(t->sampleRate);
// ramp gain - resample to temp buffer and scale/mix in 2nd step
- if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+ if (aux != NULL) {
+ // always resample with unity gain when sending to auxiliary buffer to be able
+ // to apply send level after resampling
+ // TODO: modify each resampler to support aux channel?
t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
- volumeRampStereo(t, out, outFrameCount, temp);
- }
+ if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
+ volumeRampStereo(t, out, outFrameCount, temp, aux);
+ } else {
+ volumeStereo(t, out, outFrameCount, temp, aux);
+ }
+ } else {
+ if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+ t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
+ memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
+ t->resampler->resample(temp, outFrameCount, t->bufferProvider);
+ volumeRampStereo(t, out, outFrameCount, temp, aux);
+ }
- // constant gain
- else {
- t->resampler->setVolume(t->volume[0], t->volume[1]);
- t->resampler->resample(out, outFrameCount, t->bufferProvider);
+ // constant gain
+ else {
+ t->resampler->setVolume(t->volume[0], t->volume[1]);
+ t->resampler->resample(out, outFrameCount, t->bufferProvider);
+ }
}
}
-void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp)
+void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
{
}
-void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
+void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
{
int32_t vl = t->prevVolume[0];
int32_t vr = t->prevVolume[1];
@@ -514,98 +586,238 @@
//LOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
// t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
// (vl + vlInc*frameCount)/65536.0f, frameCount);
-
+
// ramp volume
- do {
- *out++ += (vl >> 16) * (*temp++ >> 12);
- *out++ += (vr >> 16) * (*temp++ >> 12);
- vl += vlInc;
- vr += vrInc;
- } while (--frameCount);
-
- t->prevVolume[0] = vl;
- t->prevVolume[1] = vr;
- t->adjustVolumeRamp();
-}
-
-void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
-{
- int16_t const *in = static_cast<int16_t const *>(t->in);
-
- // ramp gain
- if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
- int32_t vl = t->prevVolume[0];
- int32_t vr = t->prevVolume[1];
- const int32_t vlInc = t->volumeInc[0];
- const int32_t vrInc = t->volumeInc[1];
-
- // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
- // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
- // (vl + vlInc*frameCount)/65536.0f, frameCount);
+ if UNLIKELY(aux != NULL) {
+ int32_t va = t->prevAuxLevel;
+ const int32_t vaInc = t->auxInc;
+ int32_t l;
+ int32_t r;
do {
- *out++ += (vl >> 16) * (int32_t) *in++;
- *out++ += (vr >> 16) * (int32_t) *in++;
+ l = (*temp++ >> 12);
+ r = (*temp++ >> 12);
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * r;
+ *aux++ += (va >> 17) * (l + r);
+ vl += vlInc;
+ vr += vrInc;
+ va += vaInc;
+ } while (--frameCount);
+ t->prevAuxLevel = va;
+ } else {
+ do {
+ *out++ += (vl >> 16) * (*temp++ >> 12);
+ *out++ += (vr >> 16) * (*temp++ >> 12);
vl += vlInc;
vr += vrInc;
} while (--frameCount);
-
- t->prevVolume[0] = vl;
- t->prevVolume[1] = vr;
- t->adjustVolumeRamp();
}
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->adjustVolumeRamp((aux != NULL));
+}
- // constant gain
- else {
- const uint32_t vrl = t->volumeRL;
+void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
+{
+ const int16_t vl = t->volume[0];
+ const int16_t vr = t->volume[1];
+
+ if UNLIKELY(aux != NULL) {
+ const int16_t va = (int16_t)t->auxLevel;
do {
- uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
- in += 2;
- out[0] = mulAddRL(1, rl, vrl, out[0]);
- out[1] = mulAddRL(0, rl, vrl, out[1]);
+ int16_t l = (int16_t)(*temp++ >> 12);
+ int16_t r = (int16_t)(*temp++ >> 12);
+ out[0] = mulAdd(l, vl, out[0]);
+ int16_t a = (int16_t)(((int32_t)l + r) >> 1);
+ out[1] = mulAdd(r, vr, out[1]);
+ out += 2;
+ aux[0] = mulAdd(a, va, aux[0]);
+ aux++;
+ } while (--frameCount);
+ } else {
+ do {
+ int16_t l = (int16_t)(*temp++ >> 12);
+ int16_t r = (int16_t)(*temp++ >> 12);
+ out[0] = mulAdd(l, vl, out[0]);
+ out[1] = mulAdd(r, vr, out[1]);
out += 2;
} while (--frameCount);
}
+}
+
+void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
+{
+ int16_t const *in = static_cast<int16_t const *>(t->in);
+
+ if UNLIKELY(aux != NULL) {
+ int32_t l;
+ int32_t r;
+ // ramp gain
+ if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
+ int32_t vl = t->prevVolume[0];
+ int32_t vr = t->prevVolume[1];
+ int32_t va = t->prevAuxLevel;
+ const int32_t vlInc = t->volumeInc[0];
+ const int32_t vrInc = t->volumeInc[1];
+ const int32_t vaInc = t->auxInc;
+ // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ l = (int32_t)*in++;
+ r = (int32_t)*in++;
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * r;
+ *aux++ += (va >> 17) * (l + r);
+ vl += vlInc;
+ vr += vrInc;
+ va += vaInc;
+ } while (--frameCount);
+
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->prevAuxLevel = va;
+ t->adjustVolumeRamp(true);
+ }
+
+ // constant gain
+ else {
+ const uint32_t vrl = t->volumeRL;
+ const int16_t va = (int16_t)t->auxLevel;
+ do {
+ uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+ int16_t a = (int16_t)(((int32_t)in[0] + in[1]) >> 1);
+ in += 2;
+ out[0] = mulAddRL(1, rl, vrl, out[0]);
+ out[1] = mulAddRL(0, rl, vrl, out[1]);
+ out += 2;
+ aux[0] = mulAdd(a, va, aux[0]);
+ aux++;
+ } while (--frameCount);
+ }
+ } else {
+ // ramp gain
+ if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+ int32_t vl = t->prevVolume[0];
+ int32_t vr = t->prevVolume[1];
+ const int32_t vlInc = t->volumeInc[0];
+ const int32_t vrInc = t->volumeInc[1];
+
+ // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ *out++ += (vl >> 16) * (int32_t) *in++;
+ *out++ += (vr >> 16) * (int32_t) *in++;
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->adjustVolumeRamp(false);
+ }
+
+ // constant gain
+ else {
+ const uint32_t vrl = t->volumeRL;
+ do {
+ uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+ in += 2;
+ out[0] = mulAddRL(1, rl, vrl, out[0]);
+ out[1] = mulAddRL(0, rl, vrl, out[1]);
+ out += 2;
+ } while (--frameCount);
+ }
+ }
t->in = in;
}
-void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
+void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
{
int16_t const *in = static_cast<int16_t const *>(t->in);
- // ramp gain
- if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
- int32_t vl = t->prevVolume[0];
- int32_t vr = t->prevVolume[1];
- const int32_t vlInc = t->volumeInc[0];
- const int32_t vrInc = t->volumeInc[1];
+ if UNLIKELY(aux != NULL) {
+ // ramp gain
+ if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
+ int32_t vl = t->prevVolume[0];
+ int32_t vr = t->prevVolume[1];
+ int32_t va = t->prevAuxLevel;
+ const int32_t vlInc = t->volumeInc[0];
+ const int32_t vrInc = t->volumeInc[1];
+ const int32_t vaInc = t->auxInc;
- // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
- // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
- // (vl + vlInc*frameCount)/65536.0f, frameCount);
+ // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
- do {
- int32_t l = *in++;
- *out++ += (vl >> 16) * l;
- *out++ += (vr >> 16) * l;
- vl += vlInc;
- vr += vrInc;
- } while (--frameCount);
-
- t->prevVolume[0] = vl;
- t->prevVolume[1] = vr;
- t->adjustVolumeRamp();
- }
- // constant gain
- else {
- const int16_t vl = t->volume[0];
- const int16_t vr = t->volume[1];
- do {
- int16_t l = *in++;
- out[0] = mulAdd(l, vl, out[0]);
- out[1] = mulAdd(l, vr, out[1]);
- out += 2;
- } while (--frameCount);
+ do {
+ int32_t l = *in++;
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * l;
+ *aux++ += (va >> 16) * l;
+ vl += vlInc;
+ vr += vrInc;
+ va += vaInc;
+ } while (--frameCount);
+
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->prevAuxLevel = va;
+ t->adjustVolumeRamp(true);
+ }
+ // constant gain
+ else {
+ const int16_t vl = t->volume[0];
+ const int16_t vr = t->volume[1];
+ const int16_t va = (int16_t)t->auxLevel;
+ do {
+ int16_t l = *in++;
+ out[0] = mulAdd(l, vl, out[0]);
+ out[1] = mulAdd(l, vr, out[1]);
+ out += 2;
+ aux[0] = mulAdd(l, va, aux[0]);
+ aux++;
+ } while (--frameCount);
+ }
+ } else {
+ // ramp gain
+ if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+ int32_t vl = t->prevVolume[0];
+ int32_t vr = t->prevVolume[1];
+ const int32_t vlInc = t->volumeInc[0];
+ const int32_t vrInc = t->volumeInc[1];
+
+ // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ int32_t l = *in++;
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * l;
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->adjustVolumeRamp(false);
+ }
+ // constant gain
+ else {
+ const int16_t vl = t->volume[0];
+ const int16_t vr = t->volume[1];
+ do {
+ int16_t l = *in++;
+ out[0] = mulAdd(l, vl, out[0]);
+ out[1] = mulAdd(l, vr, out[1]);
+ out += 2;
+ } while (--frameCount);
+ }
}
t->in = in;
}
@@ -624,37 +836,56 @@
}
// no-op case
-void AudioMixer::process__nop(state_t* state, void* output)
+void AudioMixer::process__nop(state_t* state)
{
- // this assumes output 16 bits stereo, no resampling
- memset(output, 0, state->frameCount*4);
- uint32_t en = state->enabledTracks;
- while (en) {
- const int i = 31 - __builtin_clz(en);
- en &= ~(1<<i);
- track_t& t = state->tracks[i];
- size_t outFrames = state->frameCount;
- while (outFrames) {
- t.buffer.frameCount = outFrames;
- t.bufferProvider->getNextBuffer(&t.buffer);
- if (!t.buffer.raw) break;
- outFrames -= t.buffer.frameCount;
- t.bufferProvider->releaseBuffer(&t.buffer);
+ uint32_t e0 = state->enabledTracks;
+ size_t bufSize = state->frameCount * sizeof(int16_t) * MAX_NUM_CHANNELS;
+ while (e0) {
+ // process by group of tracks with same output buffer to
+ // avoid multiple memset() on same buffer
+ uint32_t e1 = e0, e2 = e0;
+ int i = 31 - __builtin_clz(e1);
+ track_t& t1 = state->tracks[i];
+ e2 &= ~(1<<i);
+ while (e2) {
+ i = 31 - __builtin_clz(e2);
+ e2 &= ~(1<<i);
+ track_t& t2 = state->tracks[i];
+ if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+ e1 &= ~(1<<i);
+ }
+ }
+ e0 &= ~(e1);
+
+ memset(t1.mainBuffer, 0, bufSize);
+
+ while (e1) {
+ i = 31 - __builtin_clz(e1);
+ e1 &= ~(1<<i);
+ t1 = state->tracks[i];
+ size_t outFrames = state->frameCount;
+ while (outFrames) {
+ t1.buffer.frameCount = outFrames;
+ t1.bufferProvider->getNextBuffer(&t1.buffer);
+ if (!t1.buffer.raw) break;
+ outFrames -= t1.buffer.frameCount;
+ t1.bufferProvider->releaseBuffer(&t1.buffer);
+ }
}
}
}
// generic code without resampling
-void AudioMixer::process__genericNoResampling(state_t* state, void* output)
+void AudioMixer::process__genericNoResampling(state_t* state)
{
int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32)));
// acquire each track's buffer
uint32_t enabledTracks = state->enabledTracks;
- uint32_t en = enabledTracks;
- while (en) {
- const int i = 31 - __builtin_clz(en);
- en &= ~(1<<i);
+ uint32_t e0 = enabledTracks;
+ while (e0) {
+ const int i = 31 - __builtin_clz(e0);
+ e0 &= ~(1<<i);
track_t& t = state->tracks[i];
t.buffer.frameCount = state->frameCount;
t.bufferProvider->getNextBuffer(&t.buffer);
@@ -666,110 +897,156 @@
enabledTracks &= ~(1<<i);
}
- // this assumes output 16 bits stereo, no resampling
- int32_t* out = static_cast<int32_t*>(output);
- size_t numFrames = state->frameCount;
- do {
- memset(outTemp, 0, sizeof(outTemp));
-
- en = enabledTracks;
- while (en) {
- const int i = 31 - __builtin_clz(en);
- en &= ~(1<<i);
- track_t& t = state->tracks[i];
- size_t outFrames = BLOCKSIZE;
-
- while (outFrames) {
- size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
- if (inFrames) {
- (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp);
- t.frameCount -= inFrames;
- outFrames -= inFrames;
- }
- if (t.frameCount == 0 && outFrames) {
- t.bufferProvider->releaseBuffer(&t.buffer);
- t.buffer.frameCount = numFrames - (BLOCKSIZE - outFrames);
- t.bufferProvider->getNextBuffer(&t.buffer);
- t.in = t.buffer.raw;
- if (t.in == NULL) {
- enabledTracks &= ~(1<<i);
- break;
- }
- t.frameCount = t.buffer.frameCount;
- }
+ e0 = enabledTracks;
+ while (e0) {
+ // process by group of tracks with same output buffer to
+ // optimize cache use
+ uint32_t e1 = e0, e2 = e0;
+ int j = 31 - __builtin_clz(e1);
+ track_t& t1 = state->tracks[j];
+ e2 &= ~(1<<j);
+ while (e2) {
+ j = 31 - __builtin_clz(e2);
+ e2 &= ~(1<<j);
+ track_t& t2 = state->tracks[j];
+ if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+ e1 &= ~(1<<j);
}
}
-
- ditherAndClamp(out, outTemp, BLOCKSIZE);
- out += BLOCKSIZE;
- numFrames -= BLOCKSIZE;
- } while (numFrames);
-
+ e0 &= ~(e1);
+ // this assumes output 16 bits stereo, no resampling
+ int32_t *out = t1.mainBuffer;
+ size_t numFrames = 0;
+ do {
+ memset(outTemp, 0, sizeof(outTemp));
+ e2 = e1;
+ while (e2) {
+ const int i = 31 - __builtin_clz(e2);
+ e2 &= ~(1<<i);
+ track_t& t = state->tracks[i];
+ size_t outFrames = BLOCKSIZE;
+ int32_t *aux = NULL;
+ if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+ aux = t.auxBuffer + numFrames;
+ }
+ while (outFrames) {
+ size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
+ if (inFrames) {
+ (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, aux);
+ t.frameCount -= inFrames;
+ outFrames -= inFrames;
+ if UNLIKELY(aux != NULL) {
+ aux += inFrames;
+ }
+ }
+ if (t.frameCount == 0 && outFrames) {
+ t.bufferProvider->releaseBuffer(&t.buffer);
+ t.buffer.frameCount = (state->frameCount - numFrames) - (BLOCKSIZE - outFrames);
+ t.bufferProvider->getNextBuffer(&t.buffer);
+ t.in = t.buffer.raw;
+ if (t.in == NULL) {
+ enabledTracks &= ~(1<<i);
+ e1 &= ~(1<<i);
+ break;
+ }
+ t.frameCount = t.buffer.frameCount;
+ }
+ }
+ }
+ ditherAndClamp(out, outTemp, BLOCKSIZE);
+ out += BLOCKSIZE;
+ numFrames += BLOCKSIZE;
+ } while (numFrames < state->frameCount);
+ }
// release each track's buffer
- en = enabledTracks;
- while (en) {
- const int i = 31 - __builtin_clz(en);
- en &= ~(1<<i);
+ e0 = enabledTracks;
+ while (e0) {
+ const int i = 31 - __builtin_clz(e0);
+ e0 &= ~(1<<i);
track_t& t = state->tracks[i];
t.bufferProvider->releaseBuffer(&t.buffer);
}
}
-// generic code with resampling
-void AudioMixer::process__genericResampling(state_t* state, void* output)
+
+ // generic code with resampling
+void AudioMixer::process__genericResampling(state_t* state)
{
int32_t* const outTemp = state->outputTemp;
const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
memset(outTemp, 0, size);
- int32_t* out = static_cast<int32_t*>(output);
size_t numFrames = state->frameCount;
- uint32_t en = state->enabledTracks;
- while (en) {
- const int i = 31 - __builtin_clz(en);
- en &= ~(1<<i);
- track_t& t = state->tracks[i];
-
- // this is a little goofy, on the resampling case we don't
- // acquire/release the buffers because it's done by
- // the resampler.
- if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
- (t.hook)(&t, outTemp, numFrames, state->resampleTemp);
- } else {
-
- size_t outFrames = numFrames;
-
- while (outFrames) {
- t.buffer.frameCount = outFrames;
- t.bufferProvider->getNextBuffer(&t.buffer);
- t.in = t.buffer.raw;
- // t.in == NULL can happen if the track was flushed just after having
- // been enabled for mixing.
- if (t.in == NULL) break;
-
- (t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp);
- outFrames -= t.buffer.frameCount;
- t.bufferProvider->releaseBuffer(&t.buffer);
+ uint32_t e0 = state->enabledTracks;
+ while (e0) {
+ // process by group of tracks with same output buffer
+ // to optimize cache use
+ uint32_t e1 = e0, e2 = e0;
+ int j = 31 - __builtin_clz(e1);
+ track_t& t1 = state->tracks[j];
+ e2 &= ~(1<<j);
+ while (e2) {
+ j = 31 - __builtin_clz(e2);
+ e2 &= ~(1<<j);
+ track_t& t2 = state->tracks[j];
+ if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+ e1 &= ~(1<<j);
}
}
- }
+ e0 &= ~(e1);
+ int32_t *out = t1.mainBuffer;
+ while (e1) {
+ const int i = 31 - __builtin_clz(e1);
+ e1 &= ~(1<<i);
+ track_t& t = state->tracks[i];
+ int32_t *aux = NULL;
+ if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+ aux = t.auxBuffer;
+ }
- ditherAndClamp(out, outTemp, numFrames);
+ // this is a little goofy, on the resampling case we don't
+ // acquire/release the buffers because it's done by
+ // the resampler.
+ if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
+ (t.hook)(&t, outTemp, numFrames, state->resampleTemp, aux);
+ } else {
+
+ size_t outFrames = 0;
+
+ while (outFrames < numFrames) {
+ t.buffer.frameCount = numFrames - outFrames;
+ t.bufferProvider->getNextBuffer(&t.buffer);
+ t.in = t.buffer.raw;
+ // t.in == NULL can happen if the track was flushed just after having
+ // been enabled for mixing.
+ if (t.in == NULL) break;
+
+ if UNLIKELY(aux != NULL) {
+ aux += outFrames;
+ }
+ (t.hook)(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, aux);
+ outFrames += t.buffer.frameCount;
+ t.bufferProvider->releaseBuffer(&t.buffer);
+ }
+ }
+ }
+ ditherAndClamp(out, outTemp, numFrames);
+ }
}
// one track, 16 bits stereo without resampling is the most common case
-void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* output)
+void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state)
{
const int i = 31 - __builtin_clz(state->enabledTracks);
const track_t& t = state->tracks[i];
AudioBufferProvider::Buffer& b(t.buffer);
-
- int32_t* out = static_cast<int32_t*>(output);
+
+ int32_t* out = t.mainBuffer;
size_t numFrames = state->frameCount;
-
+
const int16_t vl = t.volume[0];
const int16_t vr = t.volume[1];
const uint32_t vrl = t.volumeRL;
@@ -787,7 +1064,7 @@
return;
}
size_t outFrames = b.frameCount;
-
+
if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
// volume is boosted, so we might need to clamp even though
// we process only one track.
@@ -816,7 +1093,9 @@
}
// 2 tracks is also a common case
-void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output)
+// NEVER used in current implementation of process__validate()
+// only use if the 2 tracks have the same output buffer
+void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state)
{
int i;
uint32_t en = state->enabledTracks;
@@ -829,24 +1108,25 @@
i = 31 - __builtin_clz(en);
const track_t& t1 = state->tracks[i];
AudioBufferProvider::Buffer& b1(t1.buffer);
-
+
int16_t const *in0;
const int16_t vl0 = t0.volume[0];
const int16_t vr0 = t0.volume[1];
size_t frameCount0 = 0;
-
+
int16_t const *in1;
const int16_t vl1 = t1.volume[0];
const int16_t vr1 = t1.volume[1];
size_t frameCount1 = 0;
-
- int32_t* out = static_cast<int32_t*>(output);
+
+ //FIXME: only works if two tracks use same buffer
+ int32_t* out = t0.mainBuffer;
size_t numFrames = state->frameCount;
int16_t const *buff = NULL;
-
+
while (numFrames) {
-
+
if (frameCount0 == 0) {
b0.frameCount = numFrames;
t0.bufferProvider->getNextBuffer(&b0);
@@ -875,13 +1155,13 @@
}
frameCount1 = b1.frameCount;
}
-
+
size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1;
numFrames -= outFrames;
frameCount0 -= outFrames;
frameCount1 -= outFrames;
-
+
do {
int32_t l0 = *in0++;
int32_t r0 = *in0++;
@@ -896,17 +1176,17 @@
r = clamp16(r);
*out++ = (r<<16) | (l & 0xFFFF);
} while (--outFrames);
-
+
if (frameCount0 == 0) {
t0.bufferProvider->releaseBuffer(&b0);
}
if (frameCount1 == 0) {
t1.bufferProvider->releaseBuffer(&b1);
}
- }
-
+ }
+
if (buff != NULL) {
- delete [] buff;
+ delete [] buff;
}
}
diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h
index 15766cd..aee3e17 100644
--- a/libs/audioflinger/AudioMixer.h
+++ b/libs/audioflinger/AudioMixer.h
@@ -63,11 +63,14 @@
// for target TRACK
CHANNEL_COUNT = 0x4000,
FORMAT = 0x4001,
+ MAIN_BUFFER = 0x4002,
+ AUX_BUFFER = 0x4003,
// for TARGET RESAMPLE
SAMPLE_RATE = 0x4100,
// for TARGET VOLUME (8 channels max)
VOLUME0 = 0x4200,
VOLUME1 = 0x4201,
+ AUXLEVEL = 0x4210,
};
@@ -78,10 +81,10 @@
status_t disable(int name);
status_t setActiveTrack(int track);
- status_t setParameter(int target, int name, int value);
+ status_t setParameter(int target, int name, void *value);
status_t setBufferProvider(AudioBufferProvider* bufferProvider);
- void process(void* output);
+ void process();
uint32_t trackNames() const { return mTrackNames; }
@@ -94,6 +97,7 @@
NEEDS_FORMAT__MASK = 0x000000F0,
NEEDS_MUTE__MASK = 0x00000100,
NEEDS_RESAMPLE__MASK = 0x00001000,
+ NEEDS_AUX__MASK = 0x00010000,
};
enum {
@@ -107,6 +111,9 @@
NEEDS_RESAMPLE_DISABLED = 0x00000000,
NEEDS_RESAMPLE_ENABLED = 0x00001000,
+
+ NEEDS_AUX_DISABLED = 0x00000000,
+ NEEDS_AUX_ENABLED = 0x00010000,
};
static inline int32_t applyVolume(int32_t in, int32_t v) {
@@ -115,9 +122,10 @@
struct state_t;
+ struct track_t;
- typedef void (*mix_t)(state_t* state, void* output);
-
+ typedef void (*mix_t)(state_t* state);
+ typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
static const int BLOCKSIZE = 16; // 4 cache lines
struct track_t {
@@ -131,6 +139,9 @@
int32_t prevVolume[2];
int32_t volumeInc[2];
+ int32_t auxLevel;
+ int32_t auxInc;
+ int32_t prevAuxLevel;
uint16_t frameCount;
@@ -142,15 +153,17 @@
AudioBufferProvider* bufferProvider;
mutable AudioBufferProvider::Buffer buffer;
- void (*hook)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp);
+ hook_t hook;
void const* in; // current location in buffer
AudioResampler* resampler;
uint32_t sampleRate;
+ int32_t* mainBuffer;
+ int32_t* auxBuffer;
bool setResampler(uint32_t sampleRate, uint32_t devSampleRate);
bool doesResample() const;
- void adjustVolumeRamp();
+ void adjustVolumeRamp(bool aux);
};
// pad to 32-bytes to fill cache line
@@ -173,18 +186,19 @@
void invalidateState(uint32_t mask);
- static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
- static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
- static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp);
- static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
- static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
+ static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+ static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+ static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+ static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+ static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
+ static void volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
- static void process__validate(state_t* state, void* output);
- static void process__nop(state_t* state, void* output);
- static void process__genericNoResampling(state_t* state, void* output);
- static void process__genericResampling(state_t* state, void* output);
- static void process__OneTrack16BitsStereoNoResampling(state_t* state, void* output);
- static void process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output);
+ static void process__validate(state_t* state);
+ static void process__nop(state_t* state);
+ static void process__genericNoResampling(state_t* state);
+ static void process__genericResampling(state_t* state);
+ static void process__OneTrack16BitsStereoNoResampling(state_t* state);
+ static void process__TwoTracks16BitsStereoNoResampling(state_t* state);
};
// ----------------------------------------------------------------------------
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index e4d5bb3..1415493 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -71,6 +71,8 @@
const char CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED[] = "smooth-zoom-supported";
const char CameraParameters::KEY_FOCUS_DISTANCES[] = "focus-distances";
const char CameraParameters::KEY_VIDEO_FRAME_FORMAT[] = "video-frame-format";
+const char CameraParameters::KEY_METERING_MODE[] = "metering-mode";
+const char CameraParameters::KEY_SUPPORTED_METERING_MODES[] = "metering-mode-values";
const char CameraParameters::TRUE[] = "true";
const char CameraParameters::FOCUS_DISTANCE_INFINITY[] = "Infinity";
@@ -142,6 +144,11 @@
const char CameraParameters::FOCUS_MODE_EDOF[] = "edof";
const char CameraParameters::FOCUS_MODE_CONTINUOUS[] = "continuous";
+// Values for metering mode settings.
+const char CameraParameters::METERING_MODE_CENTER_WEIGHTED[] = "center-weighted";
+const char CameraParameters::METERING_MODE_FRAME_AVERAGE[] = "frame-average";
+const char CameraParameters::METERING_MODE_SPOT[] = "spot";
+
CameraParameters::CameraParameters()
: mMap()
{
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 8f6564a..7672f0f 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -344,7 +344,9 @@
* <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
* PlaybackCompleted}</p></td>
* <td>{Error}</p></td>
- * <td>Successful invoke of this method does not change the state.</p></td></tr>
+ * <td>Successful invoke of this method does not change the state. In order for the
+ * target audio stream type to become effective, this method must be called before
+ * prepare() or prepareAsync().</p></td></tr>
* <tr><td>setDataSource </p></td>
* <td>{Idle} </p></td>
* <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
@@ -1112,7 +1114,9 @@
/**
* Sets the audio stream type for this MediaPlayer. See {@link AudioManager}
- * for a list of stream types.
+ * for a list of stream types. Must call this method before prepare() or
+ * prepareAsync() in order for the target stream type to become effective
+ * thereafter.
*
* @param streamtype the audio stream type
* @see android.media.AudioManager
diff --git a/media/libeffects/EffectEqualizer.cpp b/media/libeffects/EffectEqualizer.cpp
index c08f4f5..e39e595 100644
--- a/media/libeffects/EffectEqualizer.cpp
+++ b/media/libeffects/EffectEqualizer.cpp
@@ -114,7 +114,7 @@
//--- Effect Library Interface Implementation
//
-extern "C" int EffectQueryNumberEffects(int *pNumEffects) {
+extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) {
*pNumEffects = 1;
gEffectIndex = 0;
return 0;
diff --git a/media/libeffects/EffectReverb.c b/media/libeffects/EffectReverb.c
index 3181504..202f50b 100644
--- a/media/libeffects/EffectReverb.c
+++ b/media/libeffects/EffectReverb.c
@@ -18,7 +18,8 @@
//
#define LOG_NDEBUG 0
#include <cutils/log.h>
-
+#include <stdlib.h>
+#include <string.h>
#include <stdbool.h>
#include "EffectReverb.h"
#include "EffectsMath.h"
@@ -86,7 +87,7 @@
/*--- Effect Library Interface Implementation ---*/
-int EffectQueryNumberEffects(int *pNumEffects) {
+int EffectQueryNumberEffects(uint32_t *pNumEffects) {
*pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)
- 1;
gEffectIndex = 0;
diff --git a/media/libeffects/EffectReverb.h b/media/libeffects/EffectReverb.h
index cd14891..578e09e 100644
--- a/media/libeffects/EffectReverb.h
+++ b/media/libeffects/EffectReverb.h
@@ -292,7 +292,7 @@
* Effect API
*------------------------------------
*/
-int EffectQueryNumberEffects(int *pNumEffects);
+int EffectQueryNumberEffects(uint32_t *pNumEffects);
int EffectQueryNext(effect_descriptor_t *pDescriptor);
int EffectCreate(effect_uuid_t *effectUID, effect_interface_t *pInterface);
int EffectRelease(effect_interface_t interface);
diff --git a/media/libeffects/EffectsFactory.c b/media/libeffects/EffectsFactory.c
index 35a1001..6800765 100644
--- a/media/libeffects/EffectsFactory.c
+++ b/media/libeffects/EffectsFactory.c
@@ -39,7 +39,7 @@
static int init();
static int loadLibrary(const char *libPath, int *handle);
static int unloadLibrary(int handle);
-static int numEffectModules();
+static uint32_t numEffectModules();
static int findEffect(effect_uuid_t *uuid, lib_entry_t **lib, effect_descriptor_t **desc);
static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
@@ -96,7 +96,7 @@
// Effect Factory Interface functions
/////////////////////////////////////////////////
-int EffectQueryNumberEffects(int *pNumEffects)
+int EffectQueryNumberEffects(uint32_t *pNumEffects)
{
int ret = init();
if (ret < 0) {
@@ -353,8 +353,8 @@
effect_QueryNextEffect_t queryFx;
effect_CreateEffect_t createFx;
effect_ReleaseEffect_t releaseFx;
- int numFx;
- int fx;
+ uint32_t numFx;
+ uint32_t fx;
int ret;
list_elem_t *e, *descHead = NULL;
lib_entry_t *l;
@@ -525,9 +525,9 @@
-int numEffectModules() {
+uint32_t numEffectModules() {
list_elem_t *e = gLibraryList;
- int cnt = 0;
+ uint32_t cnt = 0;
// Reset pointers for EffectQueryNext()
gCurLib = e;
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index fd2b1ce..a2436ab 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -45,7 +45,7 @@
// ---------------------------------------------------------------------------
AudioRecord::AudioRecord()
- : mStatus(NO_INIT)
+ : mStatus(NO_INIT), mSessionId(0)
{
}
@@ -58,11 +58,12 @@
uint32_t flags,
callback_t cbf,
void* user,
- int notificationFrames)
- : mStatus(NO_INIT)
+ int notificationFrames,
+ int sessionId)
+ : mStatus(NO_INIT), mSessionId(0)
{
mStatus = set(inputSource, sampleRate, format, channels,
- frameCount, flags, cbf, user, notificationFrames);
+ frameCount, flags, cbf, user, notificationFrames, sessionId);
}
AudioRecord::~AudioRecord()
@@ -91,7 +92,8 @@
callback_t cbf,
void* user,
int notificationFrames,
- bool threadCanCallJava)
+ bool threadCanCallJava,
+ int sessionId)
{
LOGV("set(): sampleRate %d, channels %d, frameCount %d",sampleRate, channels, frameCount);
@@ -119,6 +121,7 @@
if (!AudioSystem::isInputChannel(channels)) {
return BAD_VALUE;
}
+
int channelCount = AudioSystem::popCount(channels);
audio_io_handle_t input = AudioSystem::getInput(inputSource,
@@ -164,6 +167,8 @@
notificationFrames = frameCount/2;
}
+ mSessionId = sessionId;
+
// create the IAudioRecord
status_t status = openRecord(sampleRate, format, channelCount,
frameCount, flags, input);
@@ -414,6 +419,7 @@
channelCount,
frameCount,
((uint16_t)flags) << 16,
+ &mSessionId,
&status);
if (record == 0) {
LOGE("AudioFlinger could not create record track, status: %d", status);
@@ -532,6 +538,11 @@
return mInput;
}
+int AudioRecord::getSessionId()
+{
+ return mSessionId;
+}
+
// -------------------------------------------------------------------------
ssize_t AudioRecord::read(void* buffer, size_t userSize)
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 5e6ce42..c77f551 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -364,6 +364,12 @@
return result;
}
+int AudioSystem::newAudioSessionId() {
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) return 0;
+ return af->newAudioSessionId();
+}
+
// ---------------------------------------------------------------------------
void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index c350532..4b61131 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -58,7 +58,8 @@
uint32_t flags,
callback_t cbf,
void* user,
- int notificationFrames)
+ int notificationFrames,
+ int sessionId)
: mStatus(NO_INIT)
{
mStatus = set(streamType, sampleRate, format, channels,
@@ -74,7 +75,8 @@
uint32_t flags,
callback_t cbf,
void* user,
- int notificationFrames)
+ int notificationFrames,
+ int sessionId)
: mStatus(NO_INIT)
{
mStatus = set(streamType, sampleRate, format, channels,
@@ -110,7 +112,8 @@
void* user,
int notificationFrames,
const sp<IMemory>& sharedBuffer,
- bool threadCanCallJava)
+ bool threadCanCallJava,
+ int sessionId)
{
LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
@@ -171,8 +174,11 @@
mVolume[LEFT] = 1.0f;
mVolume[RIGHT] = 1.0f;
+ mSendLevel = 0;
mFrameCount = frameCount;
mNotificationFramesReq = notificationFrames;
+ mSessionId = sessionId;
+
// create the IAudioTrack
status_t status = createTrack(streamType, sampleRate, format, channelCount,
frameCount, flags, sharedBuffer, output, true);
@@ -396,19 +402,49 @@
return mMuted;
}
-void AudioTrack::setVolume(float left, float right)
+status_t AudioTrack::setVolume(float left, float right)
{
+ if (left > 1.0f || right > 1.0f) {
+ return BAD_VALUE;
+ }
+
mVolume[LEFT] = left;
mVolume[RIGHT] = right;
// write must be atomic
- mCblk->volumeLR = (int32_t(int16_t(left * 0x1000)) << 16) | int16_t(right * 0x1000);
+ mCblk->volumeLR = (uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000);
+
+ return NO_ERROR;
}
void AudioTrack::getVolume(float* left, float* right)
{
- *left = mVolume[LEFT];
- *right = mVolume[RIGHT];
+ if (left != NULL) {
+ *left = mVolume[LEFT];
+ }
+ if (right != NULL) {
+ *right = mVolume[RIGHT];
+ }
+}
+
+status_t AudioTrack::setSendLevel(float level)
+{
+ if (level > 1.0f) {
+ return BAD_VALUE;
+ }
+
+ mSendLevel = level;
+
+ mCblk->sendLevel = uint16_t(level * 0x1000);
+
+ return NO_ERROR;
+}
+
+void AudioTrack::getSendLevel(float* level)
+{
+ if (level != NULL) {
+ *level = mSendLevel;
+ }
}
status_t AudioTrack::setSampleRate(int rate)
@@ -563,6 +599,16 @@
mCblk->sampleRate, mFormat, mChannels, (AudioSystem::output_flags)mFlags);
}
+int AudioTrack::getSessionId()
+{
+ return mSessionId;
+}
+
+status_t AudioTrack::attachAuxEffect(int effectId)
+{
+ return mAudioTrack->attachAuxEffect(effectId);
+}
+
// -------------------------------------------------------------------------
status_t AudioTrack::createTrack(
@@ -647,6 +693,7 @@
((uint16_t)flags) << 16,
sharedBuffer,
output,
+ &mSessionId,
&status);
if (track == 0) {
@@ -672,7 +719,8 @@
mCblk->stepUser(mCblk->frameCount);
}
- mCblk->volumeLR = (int32_t(int16_t(mVolume[LEFT] * 0x1000)) << 16) | int16_t(mVolume[RIGHT] * 0x1000);
+ mCblk->volumeLR = (uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000);
+ mCblk->sendLevel = uint16_t(mSendLevel * 0x1000);
mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
mCblk->waitTimeMs = 0;
mRemainingFrames = mNotificationFramesAct;
@@ -1016,7 +1064,7 @@
: lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
userBase(0), serverBase(0), buffers(0), frameCount(0),
loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
- flags(0)
+ flags(0), sendLevel(0)
{
}
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 47bcc12..f2a8db3 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -62,7 +62,14 @@
SET_STREAM_OUTPUT,
SET_VOICE_VOLUME,
GET_RENDER_POSITION,
- GET_INPUT_FRAMES_LOST
+ GET_INPUT_FRAMES_LOST,
+ NEW_AUDIO_SESSION_ID,
+ LOAD_EFFECT_LIBRARY,
+ UNLOAD_EFFECT_LIBRARY,
+ QUERY_NUM_EFFECTS,
+ QUERY_NEXT_EFFECT,
+ GET_EFFECT_DESCRIPTOR,
+ CREATE_EFFECT
};
class BpAudioFlinger : public BpInterface<IAudioFlinger>
@@ -83,6 +90,7 @@
uint32_t flags,
const sp<IMemory>& sharedBuffer,
int output,
+ int *sessionId,
status_t *status)
{
Parcel data, reply;
@@ -97,10 +105,19 @@
data.writeInt32(flags);
data.writeStrongBinder(sharedBuffer->asBinder());
data.writeInt32(output);
+ int lSessionId = 0;
+ if (sessionId != NULL) {
+ lSessionId = *sessionId;
+ }
+ data.writeInt32(lSessionId);
status_t lStatus = remote()->transact(CREATE_TRACK, data, &reply);
if (lStatus != NO_ERROR) {
LOGE("createTrack error: %s", strerror(-lStatus));
} else {
+ lSessionId = reply.readInt32();
+ if (sessionId != NULL) {
+ *sessionId = lSessionId;
+ }
lStatus = reply.readInt32();
track = interface_cast<IAudioTrack>(reply.readStrongBinder());
}
@@ -118,6 +135,7 @@
int channelCount,
int frameCount,
uint32_t flags,
+ int *sessionId,
status_t *status)
{
Parcel data, reply;
@@ -130,10 +148,19 @@
data.writeInt32(channelCount);
data.writeInt32(frameCount);
data.writeInt32(flags);
+ int lSessionId = 0;
+ if (sessionId != NULL) {
+ lSessionId = *sessionId;
+ }
+ data.writeInt32(lSessionId);
status_t lStatus = remote()->transact(OPEN_RECORD, data, &reply);
if (lStatus != NO_ERROR) {
LOGE("openRecord error: %s", strerror(-lStatus));
} else {
+ lSessionId = reply.readInt32();
+ if (sessionId != NULL) {
+ *sessionId = lSessionId;
+ }
lStatus = reply.readInt32();
record = interface_cast<IAudioRecord>(reply.readStrongBinder());
}
@@ -497,6 +524,157 @@
remote()->transact(GET_INPUT_FRAMES_LOST, data, &reply);
return reply.readInt32();
}
+
+ virtual int newAudioSessionId()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ status_t status = remote()->transact(NEW_AUDIO_SESSION_ID, data, &reply);
+ int id = 0;
+ if (status == NO_ERROR) {
+ id = reply.readInt32();
+ }
+ return id;
+ }
+
+ virtual status_t loadEffectLibrary(const char *libPath, int *handle)
+ {
+ if (libPath == NULL || handle == NULL) {
+ return BAD_VALUE;
+ }
+ *handle = 0;
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeCString(libPath);
+ status_t status = remote()->transact(LOAD_EFFECT_LIBRARY, data, &reply);
+ if (status == NO_ERROR) {
+ status = reply.readInt32();
+ if (status == NO_ERROR) {
+ *handle = reply.readInt32();
+ }
+ }
+ return status;
+ }
+
+ virtual status_t unloadEffectLibrary(int handle)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(handle);
+ status_t status = remote()->transact(UNLOAD_EFFECT_LIBRARY, data, &reply);
+ if (status == NO_ERROR) {
+ status = reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t queryNumberEffects(uint32_t *numEffects)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ status_t status = remote()->transact(QUERY_NUM_EFFECTS, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = reply.readInt32();
+ if (status != NO_ERROR) {
+ return status;
+ }
+ if (numEffects) {
+ *numEffects = (uint32_t)reply.readInt32();
+ }
+ return NO_ERROR;
+ }
+
+ virtual status_t queryNextEffect(effect_descriptor_t *pDescriptor)
+ {
+ if (pDescriptor == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ status_t status = remote()->transact(QUERY_NEXT_EFFECT, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = reply.readInt32();
+ if (status != NO_ERROR) {
+ return status;
+ }
+ reply.read(pDescriptor, sizeof(effect_descriptor_t));
+ return NO_ERROR;
+ }
+
+ virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *pDescriptor)
+ {
+ if (pUuid == NULL || pDescriptor == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.write(pUuid, sizeof(effect_uuid_t));
+ status_t status = remote()->transact(GET_EFFECT_DESCRIPTOR, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = reply.readInt32();
+ if (status != NO_ERROR) {
+ return status;
+ }
+ reply.read(pDescriptor, sizeof(effect_descriptor_t));
+ return NO_ERROR;
+ }
+
+ virtual sp<IEffect> createEffect(pid_t pid,
+ effect_descriptor_t *pDesc,
+ const sp<IEffectClient>& client,
+ int32_t priority,
+ int output,
+ int sessionId,
+ status_t *status,
+ int *id,
+ int *enabled)
+ {
+ Parcel data, reply;
+ sp<IEffect> effect;
+
+ if (pDesc == NULL) {
+ return effect;
+ if (status) {
+ *status = BAD_VALUE;
+ }
+ }
+
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(pid);
+ data.write(pDesc, sizeof(effect_descriptor_t));
+ data.writeStrongBinder(client->asBinder());
+ data.writeInt32(priority);
+ data.writeInt32(output);
+ data.writeInt32(sessionId);
+
+ status_t lStatus = remote()->transact(CREATE_EFFECT, data, &reply);
+ if (lStatus != NO_ERROR) {
+ LOGE("createEffect error: %s", strerror(-lStatus));
+ } else {
+ lStatus = reply.readInt32();
+ int tmp = reply.readInt32();
+ if (id) {
+ *id = tmp;
+ }
+ tmp = reply.readInt32();
+ if (enabled) {
+ *enabled = tmp;
+ }
+ effect = interface_cast<IEffect>(reply.readStrongBinder());
+ reply.read(pDesc, sizeof(effect_descriptor_t));
+ }
+ if (status) {
+ *status = lStatus;
+ }
+
+ return effect;
+ }
};
IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger");
@@ -518,10 +696,12 @@
uint32_t flags = data.readInt32();
sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder());
int output = data.readInt32();
+ int sessionId = data.readInt32();
status_t status;
sp<IAudioTrack> track = createTrack(pid,
streamType, sampleRate, format,
- channelCount, bufferCount, flags, buffer, output, &status);
+ channelCount, bufferCount, flags, buffer, output, &sessionId, &status);
+ reply->writeInt32(sessionId);
reply->writeInt32(status);
reply->writeStrongBinder(track->asBinder());
return NO_ERROR;
@@ -535,9 +715,11 @@
int channelCount = data.readInt32();
size_t bufferCount = data.readInt32();
uint32_t flags = data.readInt32();
+ int sessionId = data.readInt32();
status_t status;
sp<IAudioRecord> record = openRecord(pid, input,
- sampleRate, format, channelCount, bufferCount, flags, &status);
+ sampleRate, format, channelCount, bufferCount, flags, &sessionId, &status);
+ reply->writeInt32(sessionId);
reply->writeInt32(status);
reply->writeStrongBinder(record->asBinder());
return NO_ERROR;
@@ -768,7 +950,79 @@
reply->writeInt32(getInputFramesLost(ioHandle));
return NO_ERROR;
} break;
+ case NEW_AUDIO_SESSION_ID: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ reply->writeInt32(newAudioSessionId());
+ return NO_ERROR;
+ } break;
+ case LOAD_EFFECT_LIBRARY: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ int handle;
+ status_t status = loadEffectLibrary(data.readCString(), &handle);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->writeInt32(handle);
+ }
+ return NO_ERROR;
+ }
+ case UNLOAD_EFFECT_LIBRARY: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ reply->writeInt32(unloadEffectLibrary(data.readInt32()));
+ return NO_ERROR;
+ }
+ case QUERY_NUM_EFFECTS: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ uint32_t numEffects;
+ status_t status = queryNumberEffects(&numEffects);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->writeInt32((int32_t)numEffects);
+ }
+ return NO_ERROR;
+ }
+ case QUERY_NEXT_EFFECT: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ effect_descriptor_t desc;
+ status_t status = queryNextEffect(&desc);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->write(&desc, sizeof(effect_descriptor_t));
+ }
+ return NO_ERROR;
+ }
+ case GET_EFFECT_DESCRIPTOR: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ effect_uuid_t uuid;
+ data.read(&uuid, sizeof(effect_uuid_t));
+ effect_descriptor_t desc;
+ status_t status = getEffectDescriptor(&uuid, &desc);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->write(&desc, sizeof(effect_descriptor_t));
+ }
+ return NO_ERROR;
+ }
+ case CREATE_EFFECT: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ pid_t pid = data.readInt32();
+ effect_descriptor_t desc;
+ data.read(&desc, sizeof(effect_descriptor_t));
+ sp<IEffectClient> client = interface_cast<IEffectClient>(data.readStrongBinder());
+ int32_t priority = data.readInt32();
+ int output = data.readInt32();
+ int sessionId = data.readInt32();
+ status_t status;
+ int id;
+ int enabled;
+ sp<IEffect> effect = createEffect(pid, &desc, client, priority, output, sessionId, &status, &id, &enabled);
+ reply->writeInt32(status);
+ reply->writeInt32(id);
+ reply->writeInt32(enabled);
+ reply->writeStrongBinder(effect->asBinder());
+ reply->write(&desc, sizeof(effect_descriptor_t));
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index 01ffd75..bc8ff34 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -34,7 +34,8 @@
STOP,
FLUSH,
MUTE,
- PAUSE
+ PAUSE,
+ ATTACH_AUX_EFFECT
};
class BpAudioTrack : public BpInterface<IAudioTrack>
@@ -97,7 +98,21 @@
cblk = interface_cast<IMemory>(reply.readStrongBinder());
}
return cblk;
- }
+ }
+
+ virtual status_t attachAuxEffect(int effectId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+ data.writeInt32(effectId);
+ status_t status = remote()->transact(ATTACH_AUX_EFFECT, data, &reply);
+ if (status == NO_ERROR) {
+ status = reply.readInt32();
+ } else {
+ LOGW("attachAuxEffect() error: %s", strerror(-status));
+ }
+ return status;
+ }
};
IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack");
@@ -138,6 +153,11 @@
pause();
return NO_ERROR;
}
+ case ATTACH_AUX_EFFECT: {
+ CHECK_INTERFACE(IAudioTrack, data, reply);
+ reply->writeInt32(attachAuxEffect(data.readInt32()));
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp
index 944a0c1..b6ed56b 100644
--- a/media/libstagefright/Prefetcher.cpp
+++ b/media/libstagefright/Prefetcher.cpp
@@ -58,13 +58,14 @@
status_t mFinalStatus;
int64_t mSeekTimeUs;
int64_t mCacheDurationUs;
+ size_t mCacheSizeBytes;
bool mPrefetcherStopped;
bool mCurrentlyPrefetching;
List<MediaBuffer *> mCachedBuffers;
// Returns true iff source is currently caching.
- bool getCacheDurationUs(int64_t *durationUs);
+ bool getCacheDurationUs(int64_t *durationUs, size_t *totalSize = NULL);
void updateCacheDuration_l();
void clearCache_l();
@@ -125,21 +126,31 @@
return 0;
}
-// Cache about 10secs for each source.
-static int64_t kMaxCacheDurationUs = 10000000ll;
+// Cache at most 1 min for each source.
+static int64_t kMaxCacheDurationUs = 60 * 1000000ll;
+
+// At the same time cache at most 5MB per source.
+static size_t kMaxCacheSizeBytes = 5 * 1024 * 1024;
+
+// If the amount of cached data drops below this,
+// fill the cache up to the max duration again.
+static int64_t kLowWaterDurationUs = 5000000ll;
void Prefetcher::threadFunc() {
+ bool fillingCache = false;
+
for (;;) {
sp<PrefetchedSource> minSource;
+ int64_t minCacheDurationUs = -1;
{
Mutex::Autolock autoLock(mLock);
if (mDone) {
break;
}
- mCondition.waitRelative(mLock, 10000000ll);
+ mCondition.waitRelative(
+ mLock, fillingCache ? 10000000ll : 1000000000ll);
- int64_t minCacheDurationUs = -1;
ssize_t minIndex = -1;
for (size_t i = 0; i < mSources.size(); ++i) {
sp<PrefetchedSource> source = mSources[i].promote();
@@ -149,11 +160,18 @@
}
int64_t cacheDurationUs;
- if (!source->getCacheDurationUs(&cacheDurationUs)) {
+ size_t cacheSizeBytes;
+ if (!source->getCacheDurationUs(&cacheDurationUs, &cacheSizeBytes)) {
continue;
}
- if (cacheDurationUs >= kMaxCacheDurationUs) {
+ if (cacheSizeBytes > kMaxCacheSizeBytes) {
+ LOGI("max cache size reached");
+ continue;
+ }
+
+ if (mSources.size() > 1 && cacheDurationUs >= kMaxCacheDurationUs) {
+ LOGI("max duration reached, size = %d bytes", cacheSizeBytes);
continue;
}
@@ -165,14 +183,26 @@
}
if (minIndex < 0) {
+ if (fillingCache) {
+ LOGV("[%p] done filling the cache, above high water mark.",
+ this);
+ fillingCache = false;
+ }
continue;
}
}
- // Make sure not to hold the lock while calling into the source.
- // The lock guards the list of sources, not the individual sources
- // themselves.
- minSource->cacheMore();
+ if (!fillingCache && minCacheDurationUs < kLowWaterDurationUs) {
+ LOGI("[%p] cache below low water mark, filling cache.", this);
+ fillingCache = true;
+ }
+
+ if (fillingCache) {
+ // Make sure not to hold the lock while calling into the source.
+ // The lock guards the list of sources, not the individual sources
+ // themselves.
+ minSource->cacheMore();
+ }
}
Mutex::Autolock autoLock(mLock);
@@ -250,6 +280,7 @@
mReachedEOS(false),
mSeekTimeUs(0),
mCacheDurationUs(0),
+ mCacheSizeBytes(0),
mPrefetcherStopped(false),
mCurrentlyPrefetching(false) {
}
@@ -323,6 +354,7 @@
*out = *mCachedBuffers.begin();
mCachedBuffers.erase(mCachedBuffers.begin());
updateCacheDuration_l();
+ mCacheSizeBytes -= (*out)->size();
return OK;
}
@@ -331,10 +363,14 @@
return mSource->getFormat();
}
-bool PrefetchedSource::getCacheDurationUs(int64_t *durationUs) {
+bool PrefetchedSource::getCacheDurationUs(
+ int64_t *durationUs, size_t *totalSize) {
Mutex::Autolock autoLock(mLock);
*durationUs = mCacheDurationUs;
+ if (totalSize != NULL) {
+ *totalSize = mCacheSizeBytes;
+ }
if (!mStarted || mReachedEOS) {
return false;
@@ -397,6 +433,7 @@
mCachedBuffers.push_back(copy);
updateCacheDuration_l();
+ mCacheSizeBytes += copy->size();
mCurrentlyPrefetching = false;
mCondition.signal();
@@ -425,6 +462,7 @@
}
updateCacheDuration_l();
+ mCacheSizeBytes = 0;
}
void PrefetchedSource::onPrefetcherStopped() {
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
new file mode 100644
index 0000000..4c83768
--- /dev/null
+++ b/packages/SystemUI/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_JAVA_LIBRARIES := services
+
+LOCAL_PACKAGE_NAME := SystemUI
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
new file mode 100644
index 0000000..3f5b69d
--- /dev/null
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.systemui"
+ android:sharedUserId="android.uid.system">
+
+ <application
+ android:allowClearUserData="false"
+ android:label="@string/app_label"
+ android:icon="@drawable/ic_launcher_settings">
+
+ <receiver
+ android:name=".statusbar.StatusBarStarter"
+ >
+ <intent-filter>
+ <action android:name="com.android.internal.policy.statusbar.START" />
+ </intent-filter>
+ </receiver>
+ <service
+ android:name=".statusbar.PhoneStatusBarService"
+ android:exported="false"
+ />
+ </application>
+</manifest>
diff --git a/packages/SystemUI/MODULE_LICENSE_APACHE2 b/packages/SystemUI/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/SystemUI/MODULE_LICENSE_APACHE2
diff --git a/packages/SystemUI/NOTICE b/packages/SystemUI/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/packages/SystemUI/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/core/res/res/drawable-hdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png
similarity index 100%
rename from core/res/res/drawable-hdpi/battery_low_battery.png
rename to packages/SystemUI/res/drawable-hdpi/battery_low_battery.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/btn_default_small_normal.9.png b/packages/SystemUI/res/drawable-hdpi/btn_default_small_normal.9.png
new file mode 100644
index 0000000..baafed6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/btn_default_small_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/btn_default_small_normal_disable.9.png b/packages/SystemUI/res/drawable-hdpi/btn_default_small_normal_disable.9.png
new file mode 100644
index 0000000..175197b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/btn_default_small_normal_disable.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png b/packages/SystemUI/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png
new file mode 100644
index 0000000..ec1feff
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/btn_default_small_pressed.9.png b/packages/SystemUI/res/drawable-hdpi/btn_default_small_pressed.9.png
new file mode 100644
index 0000000..c1f9a0f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/btn_default_small_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/btn_default_small_selected.9.png b/packages/SystemUI/res/drawable-hdpi/btn_default_small_selected.9.png
new file mode 100644
index 0000000..0ea3f40
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/btn_default_small_selected.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_launcher_settings.png b/packages/SystemUI/res/drawable-hdpi/ic_launcher_settings.png
new file mode 100644
index 0000000..ff34a7f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_launcher_settings.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notification_overlay.9.png b/packages/SystemUI/res/drawable-hdpi/ic_notification_overlay.9.png
new file mode 100644
index 0000000..744178f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_notification_overlay.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/shade_bg.png b/packages/SystemUI/res/drawable-hdpi/shade_bg.png
new file mode 100644
index 0000000..3d00cd0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/shade_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/shade_handlebar.9.png b/packages/SystemUI/res/drawable-hdpi/shade_handlebar.9.png
new file mode 100644
index 0000000..f313ffb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/shade_handlebar.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/shade_header_background.9.png b/packages/SystemUI/res/drawable-hdpi/shade_header_background.9.png
new file mode 100644
index 0000000..37b5fef
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/shade_header_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_alarm.png b/packages/SystemUI/res/drawable-hdpi/stat_notify_alarm.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_notify_alarm.png
rename to packages/SystemUI/res/drawable-hdpi/stat_notify_alarm.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_notify_more.png b/packages/SystemUI/res/drawable-hdpi/stat_notify_more.png
new file mode 100755
index 0000000..1c7f9db
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_notify_more.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png
new file mode 100644
index 0000000..96dc085
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_1x.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_connected_1x.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_1x.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_connected_3g.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_e.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_connected_e.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_e.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_g.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_connected_g.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_h.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_connected_h.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_h.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_1x.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_in_1x.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_1x.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_in_3g.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_in_e.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_g.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_in_g.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_h.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_in_h.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_h.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_1x.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_inandout_1x.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_1x.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_inandout_3g.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_e.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_inandout_e.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_e.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_g.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_inandout_g.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_h.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_inandout_h.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_h.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_1x.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_out_1x.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_1x.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_out_3g.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_e.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_out_e.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_e.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_g.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_out_g.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_h.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_data_out_h.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_h.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_gps_acquiring.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_acquiring.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_gps_acquiring.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_gps_acquiring.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_no_sim.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_0.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_r_signal_0.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_1.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_r_signal_1.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_2.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_r_signal_2.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_3.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_r_signal_3.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_4.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_r_signal_4.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ringer_silent.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_silent.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_ringer_silent.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_silent.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ringer_vibrate.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_vibrate.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_ringer_vibrate.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_0.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_roaming_cdma_0.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_roaming_cdma_0.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_roaming_cdma_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim0.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim0.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim0.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim1.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim1.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim1.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_0.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_signal_0.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_1.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_signal_1.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_2.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_signal_2.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_3.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_signal_3.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_4.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_signal_4.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_flightmode.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_signal_flightmode.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_null.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_null.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_signal_null.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tty_mode.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_tty_mode.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_tty_mode.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_tty_mode.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_0.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_0.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_wifi_signal_0.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_1.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_wifi_signal_1.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_2.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_wifi_signal_2.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_3.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_wifi_signal_3.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_4.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_wifi_signal_4.png
rename to packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_background.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_background.9.png
new file mode 100644
index 0000000..a4be298
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_item_app_background_normal.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
new file mode 100644
index 0000000..4fbfa4f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_focus.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_focus.9.png
new file mode 100644
index 0000000..0876bc6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_focus.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_normal.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_normal.9.png
new file mode 100644
index 0000000..c01c018
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_pressed.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_pressed.9.png
new file mode 100644
index 0000000..343e4ca
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_item_background_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/title_bar_portrait.9.png b/packages/SystemUI/res/drawable-hdpi/title_bar_portrait.9.png
new file mode 100644
index 0000000..70f7cc2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/title_bar_portrait.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/title_bar_shadow.9.png b/packages/SystemUI/res/drawable-hdpi/title_bar_shadow.9.png
new file mode 100644
index 0000000..e6dab63
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
similarity index 100%
rename from core/res/res/drawable-mdpi/battery_low_battery.png
rename to packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/btn_default_small_normal.9.png b/packages/SystemUI/res/drawable-mdpi/btn_default_small_normal.9.png
new file mode 100644
index 0000000..bcedd5f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/btn_default_small_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/btn_default_small_normal_disable.9.png b/packages/SystemUI/res/drawable-mdpi/btn_default_small_normal_disable.9.png
new file mode 100644
index 0000000..ac6260f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/btn_default_small_normal_disable.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png b/packages/SystemUI/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png
new file mode 100644
index 0000000..4ee1b3f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/btn_default_small_pressed.9.png b/packages/SystemUI/res/drawable-mdpi/btn_default_small_pressed.9.png
new file mode 100644
index 0000000..25e38f4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/btn_default_small_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/btn_default_small_selected.9.png b/packages/SystemUI/res/drawable-mdpi/btn_default_small_selected.9.png
new file mode 100644
index 0000000..cc209c6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/btn_default_small_selected.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_launcher_settings.png b/packages/SystemUI/res/drawable-mdpi/ic_launcher_settings.png
new file mode 100644
index 0000000..b08ad3b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_launcher_settings.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png b/packages/SystemUI/res/drawable-mdpi/ic_notification_overlay.9.png
similarity index 76%
copy from core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png
copy to packages/SystemUI/res/drawable-mdpi/ic_notification_overlay.9.png
index 9ed20ba..1a3063c 100644
--- a/core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_notification_overlay.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/shade_bg.png b/packages/SystemUI/res/drawable-mdpi/shade_bg.png
new file mode 100644
index 0000000..941d3b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/shade_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/shade_handlebar.9.png b/packages/SystemUI/res/drawable-mdpi/shade_handlebar.9.png
new file mode 100644
index 0000000..9cbd9fe
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/shade_handlebar.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png b/packages/SystemUI/res/drawable-mdpi/shade_header_background.9.png
similarity index 76%
copy from core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png
copy to packages/SystemUI/res/drawable-mdpi/shade_header_background.9.png
index 9ed20ba..fa9a90c 100644
--- a/core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/shade_header_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_alarm.png b/packages/SystemUI/res/drawable-mdpi/stat_notify_alarm.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_notify_alarm.png
rename to packages/SystemUI/res/drawable-mdpi/stat_notify_alarm.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_notify_more.png b/packages/SystemUI/res/drawable-mdpi/stat_notify_more.png
new file mode 100644
index 0000000..e129ba9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_notify_more.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_connected_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_1x.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_connected_1x.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_1x.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_connected_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_3g.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_connected_3g.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_3g.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_connected_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_e.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_connected_e.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_e.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_connected_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_g.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_connected_g.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_g.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_connected_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_h.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_connected_h.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_h.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_in_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_1x.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_in_1x.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_1x.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_in_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_3g.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_in_3g.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_3g.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_in_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_e.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_in_e.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_e.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_in_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_g.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_in_g.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_g.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_in_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_h.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_in_h.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_h.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_inandout_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_1x.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_inandout_1x.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_1x.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_inandout_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_3g.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_inandout_3g.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_3g.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_inandout_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_e.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_inandout_e.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_e.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_inandout_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_g.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_inandout_g.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_g.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_inandout_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_h.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_inandout_h.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_h.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_out_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_1x.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_out_1x.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_1x.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_out_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_3g.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_out_3g.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_3g.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_out_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_e.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_out_e.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_e.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_out_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_g.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_out_g.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_g.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_out_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_h.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_data_out_h.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_h.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_gps_acquiring.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_gps_acquiring.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_gps_acquiring.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_gps_acquiring.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_no_sim.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_r_signal_0.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_r_signal_1.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_r_signal_2.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_r_signal_3.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_r_signal_4.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_ringer_silent.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_silent.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_ringer_silent.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_silent.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_ringer_vibrate.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_vibrate.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_ringer_vibrate.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_roaming_cdma_0.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_roaming_cdma_0.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_roaming_cdma_0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim0.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim0.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim1.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim1.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_signal_0.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_signal_1.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_signal_2.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_signal_3.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_signal_4.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_flightmode.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_signal_flightmode.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_null.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_signal_null.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tty_mode.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_tty_mode.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_tty_mode.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_tty_mode.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_wifi_signal_0.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_wifi_signal_1.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_wifi_signal_2.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_wifi_signal_3.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_wifi_signal_4.png
rename to packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_background.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_background.9.png
new file mode 100644
index 0000000..eb7c1a4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
similarity index 78%
rename from core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png
rename to packages/SystemUI/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
index 9ed20ba..c079615 100644
--- a/core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_focus.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_focus.9.png
new file mode 100644
index 0000000..c3e2415
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_focus.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_normal.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_normal.9.png
new file mode 100644
index 0000000..b8e399d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_pressed.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_pressed.9.png
new file mode 100644
index 0000000..02b4e9a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_item_background_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/title_bar_portrait.9.png b/packages/SystemUI/res/drawable-mdpi/title_bar_portrait.9.png
new file mode 100644
index 0000000..13b18d8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/title_bar_portrait.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/title_bar_shadow.9.png b/packages/SystemUI/res/drawable-mdpi/title_bar_shadow.9.png
new file mode 100644
index 0000000..dbcefee
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/btn_default_small.xml b/packages/SystemUI/res/drawable/btn_default_small.xml
new file mode 100644
index 0000000..5485ea0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/btn_default_small.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_window_focused="false" android:state_enabled="true"
+ android:drawable="@drawable/btn_default_small_normal" />
+ <item android:state_window_focused="false" android:state_enabled="false"
+ android:drawable="@drawable/btn_default_small_normal_disable" />
+ <item android:state_pressed="true"
+ android:drawable="@drawable/btn_default_small_pressed" />
+ <item android:state_focused="true" android:state_enabled="true"
+ android:drawable="@drawable/btn_default_small_selected" />
+ <item android:state_enabled="true"
+ android:drawable="@drawable/btn_default_small_normal" />
+ <item android:state_focused="true"
+ android:drawable="@drawable/btn_default_small_normal_disable_focused" />
+ <item
+ android:drawable="@drawable/btn_default_small_normal_disable" />
+</selector>
+
diff --git a/core/res/res/drawable/stat_sys_gps_acquiring_anim.xml b/packages/SystemUI/res/drawable/stat_sys_gps_acquiring_anim.xml
similarity index 90%
rename from core/res/res/drawable/stat_sys_gps_acquiring_anim.xml
rename to packages/SystemUI/res/drawable/stat_sys_gps_acquiring_anim.xml
index 954c19c..393697c 100644
--- a/core/res/res/drawable/stat_sys_gps_acquiring_anim.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_gps_acquiring_anim.xml
@@ -21,5 +21,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/stat_sys_gps_acquiring" android:duration="500" />
- <item android:drawable="@drawable/stat_sys_gps_on" android:duration="500" />
+ <item android:drawable="@*android:drawable/stat_sys_gps_on" android:duration="500" />
</animation-list>
diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_flash.xml b/packages/SystemUI/res/drawable/stat_sys_roaming_cdma_flash.xml
similarity index 100%
rename from core/res/res/drawable/stat_sys_roaming_cdma_flash.xml
rename to packages/SystemUI/res/drawable/stat_sys_roaming_cdma_flash.xml
diff --git a/core/res/res/layout/battery_low.xml b/packages/SystemUI/res/layout/battery_low.xml
similarity index 100%
rename from core/res/res/layout/battery_low.xml
rename to packages/SystemUI/res/layout/battery_low.xml
diff --git a/core/res/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
similarity index 70%
rename from core/res/res/layout/status_bar.xml
rename to packages/SystemUI/res/layout/status_bar.xml
index 2237ee4..5fe8e79 100644
--- a/core/res/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -19,8 +19,9 @@
-->
<!-- android:background="@drawable/status_bar_closed_default_background" -->
-<com.android.server.status.StatusBarView xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="@drawable/statusbar_background"
+<com.android.systemui.statusbar.StatusBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="@drawable/status_bar_background"
android:orientation="vertical"
android:focusable="true"
android:descendantFocusability="afterDescendants"
@@ -31,7 +32,7 @@
android:layout_height="match_parent"
android:orientation="horizontal">
- <com.android.server.status.IconMerger android:id="@+id/notificationIcons"
+ <com.android.systemui.statusbar.IconMerger android:id="@+id/notificationIcons"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="match_parent"
@@ -47,6 +48,17 @@
android:paddingRight="6dip"
android:gravity="center_vertical"
android:orientation="horizontal"/>
+
+ <com.android.systemui.statusbar.Clock
+ android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:singleLine="true"
+ android:paddingRight="6dip"
+ android:textSize="16sp"
+ android:textStyle="bold"
+ android:gravity="center_vertical|left"
+ />
</LinearLayout>
<LinearLayout android:id="@+id/ticker"
@@ -60,44 +72,44 @@
android:layout_height="match_parent"
android:layout_marginRight="8dip"
>
- <com.android.server.status.AnimatedImageView
+ <com.android.systemui.statusbar.AnimatedImageView
android:layout_width="25dip"
android:layout_height="25dip"
/>
- <com.android.server.status.AnimatedImageView
+ <com.android.systemui.statusbar.AnimatedImageView
android:layout_width="25dip"
android:layout_height="25dip"
/>
</ImageSwitcher>
- <com.android.server.status.TickerView android:id="@+id/tickerText"
+ <com.android.systemui.statusbar.TickerView android:id="@+id/tickerText"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="wrap_content"
android:paddingTop="2dip"
android:paddingRight="10dip">
<TextView
- android:textAppearance="@style/TextAppearance.StatusBar.Ticker"
+ android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
<TextView
- android:textAppearance="@style/TextAppearance.StatusBar.Ticker"
+ android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
- </com.android.server.status.TickerView>
+ </com.android.systemui.statusbar.TickerView>
</LinearLayout>
- <com.android.server.status.DateView android:id="@+id/date"
- android:textAppearance="@style/TextAppearance.StatusBar.Icon"
+ <com.android.systemui.statusbar.DateView android:id="@+id/date"
+ android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:singleLine="true"
android:gravity="center_vertical|left"
android:paddingLeft="6px"
android:paddingRight="6px"
- android:background="@drawable/statusbar_background"
+ android:background="@drawable/status_bar_background"
/>
-</com.android.server.status.StatusBarView>
+</com.android.systemui.statusbar.StatusBarView>
diff --git a/core/res/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
similarity index 77%
rename from core/res/res/layout/status_bar_expanded.xml
rename to packages/SystemUI/res/layout/status_bar_expanded.xml
index 68eb922..b5b1b50 100644
--- a/core/res/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -18,7 +18,7 @@
*/
-->
-<com.android.server.status.ExpandedView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.systemui.statusbar.ExpandedView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:focusable="true"
android:descendantFocusability="afterDescendants"
@@ -31,9 +31,9 @@
android:paddingTop="3dp"
android:paddingBottom="5dp"
android:paddingRight="3dp"
- android:background="@drawable/status_bar_header_background"
+ android:background="@drawable/shade_header_background"
>
- <LinearLayout
+ <com.android.systemui.statusbar.CarrierLabel
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -41,25 +41,10 @@
android:layout_marginLeft="5dp"
android:layout_gravity="center_vertical"
android:paddingBottom="1dp"
- android:orientation="vertical"
- >
- <TextView android:id="@+id/plmnLabel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textColor="?android:attr/textColorSecondary"
- android:paddingLeft="4dp"
- />
- <TextView android:id="@+id/spnLabel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textColor="?android:attr/textColorSecondary"
- android:paddingLeft="4dp"
- />
- </LinearLayout>
+ android:paddingLeft="4dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorSecondary"
+ />
<TextView android:id="@+id/clear_all_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -67,7 +52,7 @@
android:layout_marginTop="4dp"
android:layout_marginBottom="1dp"
android:textSize="14sp"
- android:textColor="?android:attr/textColorPrimaryInverse"
+ android:textColor="#ff000000"
android:text="@string/status_bar_clear_all_button"
style="?android:attr/buttonStyle"
android:paddingLeft="15dp"
@@ -87,7 +72,7 @@
android:layout_height="match_parent"
android:fadingEdge="none"
>
- <com.android.server.status.NotificationLinearLayout
+ <com.android.systemui.statusbar.NotificationLinearLayout
android:id="@+id/notificationLinearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -130,7 +115,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
/>
- </com.android.server.status.NotificationLinearLayout>
+ </com.android.systemui.statusbar.NotificationLinearLayout>
</ScrollView>
<ImageView
@@ -141,4 +126,4 @@
/>
</FrameLayout>
-</com.android.server.status.ExpandedView>
+</com.android.systemui.statusbar.ExpandedView>
diff --git a/core/res/res/layout/status_bar_icon.xml b/packages/SystemUI/res/layout/status_bar_icon.xml
similarity index 95%
rename from core/res/res/layout/status_bar_icon.xml
rename to packages/SystemUI/res/layout/status_bar_icon.xml
index 0536792..21d606f 100644
--- a/core/res/res/layout/status_bar_icon.xml
+++ b/packages/SystemUI/res/layout/status_bar_icon.xml
@@ -25,7 +25,7 @@
android:layout_height="25dp"
>
- <com.android.server.status.AnimatedImageView android:id="@+id/image"
+ <com.android.systemui.statusbar.AnimatedImageView android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
diff --git a/core/res/res/layout/status_bar_tracking.xml b/packages/SystemUI/res/layout/status_bar_tracking.xml
similarity index 77%
rename from core/res/res/layout/status_bar_tracking.xml
rename to packages/SystemUI/res/layout/status_bar_tracking.xml
index c0a7a97..a2b40e6 100644
--- a/core/res/res/layout/status_bar_tracking.xml
+++ b/packages/SystemUI/res/layout/status_bar_tracking.xml
@@ -15,7 +15,8 @@
limitations under the License.
-->
-<com.android.server.status.TrackingView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.systemui.statusbar.TrackingView
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:visibility="gone"
android:focusable="true"
@@ -25,13 +26,13 @@
android:paddingRight="0px"
>
- <com.android.server.status.TrackingPatternView
+ <com.android.systemui.statusbar.TrackingPatternView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
- <com.android.server.status.CloseDragHandle android:id="@+id/close"
+ <com.android.systemui.statusbar.CloseDragHandle android:id="@+id/close"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -41,8 +42,9 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:scaleType="fitXY"
- android:src="@drawable/status_bar_close_on"/>
+ android:src="@drawable/shade_handlebar"
+ />
- </com.android.server.status.CloseDragHandle>
+ </com.android.systemui.statusbar.CloseDragHandle>
-</com.android.server.status.TrackingView>
+</com.android.systemui.statusbar.TrackingView>
diff --git a/packages/SystemUI/res/values/arrays.xml b/packages/SystemUI/res/values/arrays.xml
new file mode 100644
index 0000000..dbb0e0f
--- /dev/null
+++ b/packages/SystemUI/res/values/arrays.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the
+ icons in the status bar that are not notifications. -->
+ <string-array name="status_bar_icon_order">
+ <item><xliff:g id="id">TODO: Remove; not used.</xliff:g></item>
+ </string-array>
+
+</resources>
diff --git a/core/res/res/drawable/status_icon_background.xml b/packages/SystemUI/res/values/config.xml
similarity index 61%
rename from core/res/res/drawable/status_icon_background.xml
rename to packages/SystemUI/res/values/config.xml
index 9846165..8ea46e5 100644
--- a/core/res/res/drawable/status_icon_background.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* //device/apps/common/res/drawable/status_icon_background.xml
-**
-** Copyright 2007, The Android Open Source Project
+/*
+** Copyright 2009, 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.
@@ -18,7 +17,10 @@
*/
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_selected="true" android:drawable="@drawable/icon_highlight_rectangle" />
- <item android:drawable="@color/transparent" />
-</selector>
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources>
+ <!-- Control whether status bar should distinguish HSPA data icon form UMTS data icon on devices -->
+ <bool name="config_hspa_data_distinguishable">false</bool>
+</resources>
+
diff --git a/packages/SystemUI/res/values/defaults.xml b/packages/SystemUI/res/values/defaults.xml
new file mode 100644
index 0000000..34302c4
--- /dev/null
+++ b/packages/SystemUI/res/values/defaults.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+ <bool name="def_dim_screen">true</bool>
+ <integer name="def_screen_off_timeout">60000</integer>
+ <bool name="def_airplane_mode_on">false</bool>
+ <!-- Comma-separated list of bluetooth, wifi, and cell. -->
+ <string name="def_airplane_mode_radios" translatable="false">cell,bluetooth,wifi</string>
+ <string name="airplane_mode_toggleable_radios" translatable="false">wifi</string>
+ <bool name="def_auto_time">true</bool>
+ <bool name="def_accelerometer_rotation">true</bool>
+ <!-- Default screen brightness, from 0 to 255. 102 is 40%. -->
+ <integer name="def_screen_brightness">102</integer>
+ <bool name="def_screen_brightness_automatic_mode">false</bool>
+ <fraction name="def_window_animation_scale">100%</fraction>
+ <fraction name="def_window_transition_scale">100%</fraction>
+ <bool name="def_haptic_feedback">true</bool>
+
+ <bool name="def_bluetooth_on">false</bool>
+ <bool name="def_install_non_market_apps">false</bool>
+ <!-- Comma-separated list of location providers.
+ Network location is off by default because it requires
+ user opt-in via Setup Wizard or Settings.
+ -->
+ <string name="def_location_providers_allowed" translatable="false">gps</string>
+ <bool name="assisted_gps_enabled">true</bool>
+ <!-- 0 == mobile, 1 == wifi. -->
+ <integer name="def_network_preference">1</integer>
+ <bool name="def_usb_mass_storage_enabled">true</bool>
+ <bool name="def_wifi_on">false</bool>
+ <bool name="def_networks_available_notification_on">true</bool>
+
+ <bool name="def_backup_enabled">false</bool>
+ <string name="def_backup_transport" translatable="false"></string>
+ <!-- Default value for whether or not to pulse the notification LED when there is a
+ pending notification -->
+ <bool name="def_notification_pulse">true</bool>
+
+ <bool name="def_mount_play_notification_snd">true</bool>
+ <bool name="def_mount_ums_autostart">false</bool>
+ <bool name="def_mount_ums_prompt">true</bool>
+ <bool name="def_mount_ums_notify_enabled">true</bool>
+ <!-- Enable User preference for setting install location -->
+ <bool name="set_install_location">true</bool>
+ <!-- Default install location if user preference for setting install location is turned on. -->
+ <integer name="def_install_location">2</integer>
+
+ <!-- user interface sound effects -->
+ <integer name="def_power_sounds_enabled">1</integer>
+ <string name="def_low_battery_sound" translatable="false">/system/media/audio/ui/LowBattery.ogg</string>
+ <integer name="def_dock_sounds_enabled">0</integer>
+ <string name="def_desk_dock_sound" translatable="false">/system/media/audio/ui/Dock.ogg</string>
+ <string name="def_desk_undock_sound" translatable="false">/system/media/audio/ui/Undock.ogg</string>
+ <string name="def_car_dock_sound" translatable="false">/system/media/audio/ui/Dock.ogg</string>
+ <string name="def_car_undock_sound" translatable="false">/system/media/audio/ui/Undock.ogg</string>
+ <integer name="def_lockscreen_sounds_enabled">0</integer>
+ <string name="def_lock_sound" translatable="false">/system/media/audio/ui/Lock.ogg</string>
+ <string name="def_unlock_sound" translatable="false">/system/media/audio/ui/Unlock.ogg</string>
+
+ <!-- Default for Settings.System.VIBRATE_IN_SILENT -->
+ <bool name="def_vibrate_in_silent">true</bool>
+</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
new file mode 100644
index 0000000..93cf377
--- /dev/null
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2006, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+ <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
+ <dimen name="status_bar_edge_ignore">5dp</dimen>
+</resources>
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
new file mode 100644
index 0000000..ba3a3d1
--- /dev/null
+++ b/packages/SystemUI/res/values/strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Name of the status bar as seen in the applications info settings page. -->
+ <string name="app_label">Status Bar</string>
+
+ <!-- The text for the button in the notification window-shade that clears
+ all of the currently visible notifications. -->
+ <string name="status_bar_clear_all_button">Clear</string>
+
+ <!-- The label in the bar at the top of the status bar when there are no notifications
+ showing. -->
+ <string name="status_bar_no_notifications_title">No notifications</string>
+
+ <!-- The label for the group of notifications for ongoing events in the opened version of
+ the status bar. An ongoing call is the prime example of this. The MP3 music player
+ might be another example. -->
+ <string name="status_bar_ongoing_events_title">Ongoing</string>
+
+ <!-- The label for the group of notifications for recent events in the opened version of
+ the status bar. Recently received text messsages (SMS), emails, calendar alerts, etc. -->
+ <string name="status_bar_latest_events_title">Notifications</string>
+
+ <!-- When the battery is low, this is displayed to the user in a dialog. The title of the low battery alert. -->
+ <string name="battery_low_title">Please connect charger</string>
+
+ <!-- When the battery is low, this is displayed to the user in a dialog. The subtitle of the low battery alert. -->
+ <string name="battery_low_subtitle">The battery is getting low:</string>
+
+ <!-- A message that appears when the battery level is getting low in a dialog. This is appened to the subtitle of the low battery alert. -->
+ <string name="battery_low_percent_format"><xliff:g id="number">%d%%</xliff:g>
+ or less remaining.</string>
+
+ <!-- When the battery is low, this is the label of the button to go to the
+ power usage activity to find out what drained the battery. -->
+ <string name="battery_low_why">Battery use</string>
+
+</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
new file mode 100644
index 0000000..10fa930
--- /dev/null
+++ b/packages/SystemUI/res/values/styles.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="TextAppearance.StatusBar.Title" parent="@android:style/TextAppearance.StatusBar">
+ <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+
+
+</resources>
diff --git a/packages/SystemUI/res/xml/bookmarks.xml b/packages/SystemUI/res/xml/bookmarks.xml
new file mode 100644
index 0000000..dfaeeaf
--- /dev/null
+++ b/packages/SystemUI/res/xml/bookmarks.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<bookmarks>
+ <bookmark
+ package="com.android.browser"
+ class="com.android.browser.BrowserActivity"
+ shortcut="b" />
+ <bookmark
+ package="com.android.contacts"
+ class="com.android.contacts.DialtactsContactsEntryActivity"
+ shortcut="c" />
+ <bookmark
+ package="com.android.email"
+ class="com.android.email.activity.Welcome"
+ shortcut="e" />
+ <bookmark
+ package="com.google.android.gm"
+ class="com.google.android.gm.ConversationListActivityGmail"
+ shortcut="g" />
+ <bookmark
+ package="com.android.im"
+ class="com.android.im.app.LandingPage"
+ shortcut="i" />
+ <bookmark
+ package="com.android.calendar"
+ class="com.android.calendar.LaunchActivity"
+ shortcut="l" />
+<!--
+ <bookmark
+ package="com.google.android.apps.maps"
+ class="com.google.android.maps.MapsActivity"
+ shortcut="m" />
+-->
+ <bookmark
+ package="com.android.music"
+ class="com.android.music.MusicBrowserActivity"
+ shortcut="p" />
+ <bookmark
+ package="com.android.mms"
+ class="com.android.mms.ui.ConversationList"
+ shortcut="s" />
+</bookmarks>
diff --git a/services/java/com/android/server/status/AnimatedImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
similarity index 97%
rename from services/java/com/android/server/status/AnimatedImageView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
index 97df065..70d4d6a 100644
--- a/services/java/com/android/server/status/AnimatedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java b/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java
new file mode 100644
index 0000000..d89d093
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2006 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.systemui.statusbar;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.provider.Telephony;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.TextView;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import com.android.internal.R;
+
+/**
+ * This widget display an analogic clock with two hands for hours and
+ * minutes.
+ */
+public class CarrierLabel extends TextView {
+ private boolean mAttached;
+
+ public CarrierLabel(Context context) {
+ this(context, null);
+ }
+
+ public CarrierLabel(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CarrierLabel(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ updateNetworkName(false, null, false, null);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (!mAttached) {
+ mAttached = true;
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
+ getContext().registerReceiver(mIntentReceiver, filter, null, getHandler());
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mAttached) {
+ getContext().unregisterReceiver(mIntentReceiver);
+ mAttached = false;
+ }
+ }
+
+ private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
+ updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
+ intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
+ }
+ }
+ };
+
+ void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
+ if (false) {
+ Slog.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
+ + " showPlmn=" + showPlmn + " plmn=" + plmn);
+ }
+ StringBuilder str = new StringBuilder();
+ boolean something = false;
+ if (showPlmn && plmn != null) {
+ str.append(plmn);
+ something = true;
+ }
+ if (showSpn && spn != null) {
+ if (something) {
+ str.append(' ');
+ }
+ str.append(spn);
+ something = true;
+ }
+ if (something) {
+ setText(str.toString());
+ } else {
+ setText(com.android.internal.R.string.lockscreen_carrier_default);
+ }
+ }
+
+
+}
+
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/Clock.java
new file mode 100644
index 0000000..9fc8df5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Clock.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2006 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.systemui.statusbar;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.format.DateFormat;
+import android.text.style.CharacterStyle;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StyleSpan;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import com.android.internal.R;
+
+/**
+ * This widget display an analogic clock with two hands for hours and
+ * minutes.
+ */
+public class Clock extends TextView {
+ private boolean mAttached;
+ private Calendar mCalendar;
+ private String mClockFormatString;
+ private SimpleDateFormat mClockFormat;
+
+ private static final int AM_PM_STYLE_NORMAL = 0;
+ private static final int AM_PM_STYLE_SMALL = 1;
+ private static final int AM_PM_STYLE_GONE = 2;
+
+ private static final int AM_PM_STYLE = AM_PM_STYLE_GONE;
+
+ public Clock(Context context) {
+ this(context, null);
+ }
+
+ public Clock(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public Clock(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (!mAttached) {
+ mAttached = true;
+ IntentFilter filter = new IntentFilter();
+
+ filter.addAction(Intent.ACTION_TIME_TICK);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+
+ getContext().registerReceiver(mIntentReceiver, filter, null, getHandler());
+ }
+
+ // NOTE: It's safe to do these after registering the receiver since the receiver always runs
+ // in the main thread, therefore the receiver can't run before this method returns.
+
+ // The time zone may have changed while the receiver wasn't registered, so update the Time
+ mCalendar = Calendar.getInstance(TimeZone.getDefault());
+
+ // Make sure we update to the current time
+ updateClock();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mAttached) {
+ getContext().unregisterReceiver(mIntentReceiver);
+ mAttached = false;
+ }
+ }
+
+ private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
+ String tz = intent.getStringExtra("time-zone");
+ mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz));
+ if (mClockFormat != null) {
+ mClockFormat.setTimeZone(mCalendar.getTimeZone());
+ }
+ }
+ updateClock();
+ }
+ };
+
+ final void updateClock() {
+ mCalendar.setTimeInMillis(System.currentTimeMillis());
+ setText(getSmallTime());
+ }
+
+ private final CharSequence getSmallTime() {
+ Context context = getContext();
+ boolean b24 = DateFormat.is24HourFormat(context);
+ int res;
+
+ if (b24) {
+ res = R.string.twenty_four_hour_time_format;
+ } else {
+ res = R.string.twelve_hour_time_format;
+ }
+
+ final char MAGIC1 = '\uEF00';
+ final char MAGIC2 = '\uEF01';
+
+ SimpleDateFormat sdf;
+ String format = context.getString(res);
+ if (!format.equals(mClockFormatString)) {
+ /*
+ * Search for an unquoted "a" in the format string, so we can
+ * add dummy characters around it to let us find it again after
+ * formatting and change its size.
+ */
+ if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) {
+ int a = -1;
+ boolean quoted = false;
+ for (int i = 0; i < format.length(); i++) {
+ char c = format.charAt(i);
+
+ if (c == '\'') {
+ quoted = !quoted;
+ }
+ if (!quoted && c == 'a') {
+ a = i;
+ break;
+ }
+ }
+
+ if (a >= 0) {
+ // Move a back so any whitespace before AM/PM is also in the alternate size.
+ final int b = a;
+ while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {
+ a--;
+ }
+ format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
+ + "a" + MAGIC2 + format.substring(b + 1);
+ }
+ }
+
+ mClockFormat = sdf = new SimpleDateFormat(format);
+ mClockFormatString = format;
+ } else {
+ sdf = mClockFormat;
+ }
+ String result = sdf.format(mCalendar.getTime());
+
+ if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) {
+ int magic1 = result.indexOf(MAGIC1);
+ int magic2 = result.indexOf(MAGIC2);
+ if (magic1 >= 0 && magic2 > magic1) {
+ SpannableStringBuilder formatted = new SpannableStringBuilder(result);
+ if (AM_PM_STYLE == AM_PM_STYLE_GONE) {
+ formatted.delete(magic1, magic2+1);
+ } else {
+ if (AM_PM_STYLE == AM_PM_STYLE_SMALL) {
+ CharacterStyle style = new RelativeSizeSpan(0.7f);
+ formatted.setSpan(style, magic1, magic2,
+ Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+ }
+ formatted.delete(magic2, magic2 + 1);
+ formatted.delete(magic1, magic1 + 1);
+ }
+ return formatted;
+ }
+ }
+
+ return result;
+
+ }
+}
+
diff --git a/services/java/com/android/server/status/CloseDragHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
similarity index 94%
rename from services/java/com/android/server/status/CloseDragHandle.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
index ad1ac4d..0f6723e 100644
--- a/services/java/com/android/server/status/CloseDragHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.content.Context;
import android.util.AttributeSet;
@@ -23,7 +23,7 @@
public class CloseDragHandle extends LinearLayout {
- StatusBarService mService;
+ PhoneStatusBarService mService;
public CloseDragHandle(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
new file mode 100644
index 0000000..f9347b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2010 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.systemui.statusbar;
+
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+
+import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIconList;
+import com.android.internal.statusbar.StatusBarNotification;
+
+/**
+ * This class takes the functions from IStatusBar that come in on
+ * binder pool threads and posts messages to get them onto the main
+ * thread, and calls onto Callbacks. It also takes care of
+ * coalescing these calls so they don't stack up. For the calls
+ * are coalesced, note that they are all idempotent.
+ */
+class CommandQueue extends IStatusBar.Stub {
+ private static final String TAG = "StatusBar.CommandQueue";
+
+ private static final int MSG_MASK = 0xffff0000;
+ private static final int INDEX_MASK = 0x0000ffff;
+
+ private static final int MSG_ICON = 0x00010000;
+ private static final int OP_SET_ICON = 1;
+ private static final int OP_REMOVE_ICON = 2;
+
+ private static final int MSG_ADD_NOTIFICATION = 0x00020000;
+ private static final int MSG_UPDATE_NOTIFICATION = 0x00030000;
+ private static final int MSG_REMOVE_NOTIFICATION = 0x00040000;
+
+ private static final int MSG_DISABLE = 0x00050000;
+
+ private static final int MSG_SET_VISIBILITY = 0x00060000;
+ private static final int OP_EXPAND = 1;
+ private static final int OP_COLLAPSE = 2;
+
+ private StatusBarIconList mList;
+ private Callbacks mCallbacks;
+ private Handler mHandler = new H();
+
+ private class NotificationQueueEntry {
+ IBinder key;
+ StatusBarNotification notification;
+ }
+
+ /**
+ * These methods are called back on the main thread.
+ */
+ public interface Callbacks {
+ public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon);
+ public void updateIcon(String slot, int index, int viewIndex,
+ StatusBarIcon old, StatusBarIcon icon);
+ public void removeIcon(String slot, int index, int viewIndex);
+ public void addNotification(IBinder key, StatusBarNotification notification);
+ public void updateNotification(IBinder key, StatusBarNotification notification);
+ public void removeNotification(IBinder key);
+ public void disable(int state);
+ public void animateExpand();
+ public void animateCollapse();
+ }
+
+ public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
+ mCallbacks = callbacks;
+ mList = list;
+ }
+
+ public void setIcon(int index, StatusBarIcon icon) {
+ synchronized (mList) {
+ int what = MSG_ICON | index;
+ mHandler.removeMessages(what);
+ mHandler.obtainMessage(what, OP_SET_ICON, 0, icon.clone()).sendToTarget();
+ }
+ }
+
+ public void removeIcon(int index) {
+ synchronized (mList) {
+ int what = MSG_ICON | index;
+ mHandler.removeMessages(what);
+ mHandler.obtainMessage(what, OP_REMOVE_ICON, 0, null).sendToTarget();
+ }
+ }
+
+ public void addNotification(IBinder key, StatusBarNotification notification) {
+ synchronized (mList) {
+ NotificationQueueEntry ne = new NotificationQueueEntry();
+ ne.key = key;
+ ne.notification = notification;
+ mHandler.obtainMessage(MSG_ADD_NOTIFICATION, 0, 0, ne).sendToTarget();
+ }
+ }
+
+ public void updateNotification(IBinder key, StatusBarNotification notification) {
+ synchronized (mList) {
+ NotificationQueueEntry ne = new NotificationQueueEntry();
+ ne.key = key;
+ ne.notification = notification;
+ mHandler.obtainMessage(MSG_UPDATE_NOTIFICATION, 0, 0, ne).sendToTarget();
+ }
+ }
+
+ public void removeNotification(IBinder key) {
+ synchronized (mList) {
+ mHandler.obtainMessage(MSG_REMOVE_NOTIFICATION, 0, 0, key).sendToTarget();
+ }
+ }
+
+ public void disable(int state) {
+ synchronized (mList) {
+ mHandler.removeMessages(MSG_DISABLE);
+ mHandler.obtainMessage(MSG_DISABLE, state, 0, null).sendToTarget();
+ }
+ }
+
+ public void animateExpand() {
+ synchronized (mList) {
+ mHandler.removeMessages(MSG_SET_VISIBILITY);
+ mHandler.obtainMessage(MSG_SET_VISIBILITY, OP_EXPAND, 0, null).sendToTarget();
+ }
+ }
+
+ public void animateCollapse() {
+ synchronized (mList) {
+ mHandler.removeMessages(MSG_SET_VISIBILITY);
+ mHandler.obtainMessage(MSG_SET_VISIBILITY, OP_COLLAPSE, 0, null).sendToTarget();
+ }
+ }
+
+ private final class H extends Handler {
+ public void handleMessage(Message msg) {
+ final int what = msg.what & MSG_MASK;
+ switch (what) {
+ case MSG_ICON: {
+ final int index = msg.what & INDEX_MASK;
+ final int viewIndex = mList.getViewIndex(index);
+ switch (msg.arg1) {
+ case OP_SET_ICON: {
+ StatusBarIcon icon = (StatusBarIcon)msg.obj;
+ StatusBarIcon old = mList.getIcon(index);
+ if (old == null) {
+ mList.setIcon(index, icon);
+ mCallbacks.addIcon(mList.getSlot(index), index, viewIndex, icon);
+ } else {
+ mList.setIcon(index, icon);
+ mCallbacks.updateIcon(mList.getSlot(index), index, viewIndex,
+ old, icon);
+ }
+ break;
+ }
+ case OP_REMOVE_ICON:
+ mList.removeIcon(index);
+ mCallbacks.removeIcon(mList.getSlot(index), index, viewIndex);
+ break;
+ }
+ break;
+ }
+ case MSG_ADD_NOTIFICATION: {
+ final NotificationQueueEntry ne = (NotificationQueueEntry)msg.obj;
+ mCallbacks.addNotification(ne.key, ne.notification);
+ break;
+ }
+ case MSG_UPDATE_NOTIFICATION: {
+ final NotificationQueueEntry ne = (NotificationQueueEntry)msg.obj;
+ mCallbacks.updateNotification(ne.key, ne.notification);
+ break;
+ }
+ case MSG_REMOVE_NOTIFICATION: {
+ mCallbacks.removeNotification((IBinder)msg.obj);
+ break;
+ }
+ case MSG_DISABLE:
+ mCallbacks.disable(msg.arg1);
+ break;
+ case MSG_SET_VISIBILITY:
+ if (msg.arg1 == OP_EXPAND) {
+ mCallbacks.animateExpand();
+ } else {
+ mCallbacks.animateCollapse();
+ }
+ }
+ }
+ }
+}
+
diff --git a/services/java/com/android/server/status/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/DateView.java
similarity index 98%
rename from services/java/com/android/server/status/DateView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/DateView.java
index c04fb45..e6d3a7e 100644
--- a/services/java/com/android/server/status/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DateView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.content.BroadcastReceiver;
import android.content.Context;
diff --git a/services/java/com/android/server/status/ExpandedView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
similarity index 84%
rename from services/java/com/android/server/status/ExpandedView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
index cb37f90..c5b901f 100644
--- a/services/java/com/android/server/status/ExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.content.Context;
import android.util.AttributeSet;
@@ -27,7 +27,7 @@
public class ExpandedView extends LinearLayout {
- StatusBarService mService;
+ PhoneStatusBarService mService;
int mPrevHeight = -1;
public ExpandedView(Context context, AttributeSet attrs) {
@@ -50,9 +50,10 @@
super.onLayout(changed, left, top, right, bottom);
int height = bottom - top;
if (height != mPrevHeight) {
- //Slog.d(StatusBarService.TAG, "height changed old=" + mPrevHeight + " new=" + height);
+ //Slog.d(PhoneStatusBarService.TAG, "height changed old=" + mPrevHeight
+ // + " new=" + height);
mPrevHeight = height;
- mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
+ mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
}
}
}
diff --git a/services/java/com/android/server/status/FixedSizeDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/FixedSizeDrawable.java
similarity index 97%
rename from services/java/com/android/server/status/FixedSizeDrawable.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/FixedSizeDrawable.java
index dbfcb2c..eb22b61 100644
--- a/services/java/com/android/server/status/FixedSizeDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FixedSizeDrawable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.graphics.drawable.Drawable;
import android.graphics.Canvas;
diff --git a/services/java/com/android/server/status/IconMerger.java b/packages/SystemUI/src/com/android/systemui/statusbar/IconMerger.java
similarity index 80%
rename from services/java/com/android/server/status/IconMerger.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/IconMerger.java
index aa702ae..027bed4 100644
--- a/services/java/com/android/server/status/IconMerger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/IconMerger.java
@@ -14,23 +14,39 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
+import android.util.Slog;
import android.view.View;
import android.widget.LinearLayout;
+import com.android.systemui.R;
+
public class IconMerger extends LinearLayout {
- StatusBarService service;
- StatusBarIcon moreIcon;
+ private static final String TAG = "IconMerger";
+
+ private StatusBarIconView mMoreView;
public IconMerger(Context context, AttributeSet attrs) {
super(context, attrs);
}
+ public void addMoreView(StatusBarIconView v, LinearLayout.LayoutParams lp) {
+ super.addView(v, lp);
+ mMoreView = v;
+ }
+
+ public void addView(StatusBarIconView v, int index, LinearLayout.LayoutParams lp) {
+ if (index == 0) {
+ throw new RuntimeException("Attempt to put view before the more view: " + v);
+ }
+ super.addView(v, index, lp);
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
@@ -50,13 +66,12 @@
}
// find the first visible one that isn't the more icon
- View moreView = null;
+ final StatusBarIconView moreView = mMoreView;
int fitLeft = -1;
int startIndex = -1;
for (i=0; i<N; i++) {
final View child = getChildAt(i);
- if (com.android.internal.R.drawable.stat_notify_more == child.getId()) {
- moreView = child;
+ if (child == moreView) {
startIndex = i+1;
}
else if (child.getVisibility() != GONE) {
@@ -66,7 +81,11 @@
}
if (moreView == null || startIndex < 0) {
- throw new RuntimeException("Status Bar / IconMerger moreView == null");
+ return;
+ /*
+ throw new RuntimeException("Status Bar / IconMerger moreView == " + moreView
+ + " startIndex=" + startIndex);
+ */
}
// if it fits without the more icon, then hide the more icon and update fitLeft
@@ -84,14 +103,14 @@
int breakingPoint = fitLeft + extra + adjust;
int number = 0;
for (i=startIndex; i<N; i++) {
- final View child = getChildAt(i);
+ final StatusBarIconView child = (StatusBarIconView)getChildAt(i);
if (child.getVisibility() != GONE) {
int childLeft = child.getLeft();
int childRight = child.getRight();
if (childLeft < breakingPoint) {
// hide this one
child.layout(0, child.getTop(), 0, child.getBottom());
- int n = this.service.getIconNumberForView(child);
+ int n = child.getStatusBarIcon().number;
if (n == 0) {
number += 1;
} else if (n > 0) {
@@ -115,7 +134,7 @@
// and provide the value later. We're the only one changing this value show it
// should be ordered correctly.
if (false) {
- this.moreIcon.update(number);
+ // TODO this.moreIcon.update(number);
} else {
mBugWorkaroundNumber = number;
mBugWorkaroundHandler.post(mBugWorkaroundRunnable);
@@ -126,8 +145,10 @@
private Handler mBugWorkaroundHandler = new Handler();
private Runnable mBugWorkaroundRunnable = new Runnable() {
public void run() {
+ /* TODO
IconMerger.this.moreIcon.update(mBugWorkaroundNumber);
IconMerger.this.moreIcon.view.invalidate();
+ */
}
};
}
diff --git a/services/java/com/android/server/status/NotificationLinearLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
similarity index 67%
copy from services/java/com/android/server/status/NotificationLinearLayout.java
copy to packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
index 2fdf956..1e89624 100644
--- a/services/java/com/android/server/status/NotificationLinearLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
@@ -14,16 +14,21 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.content.Context;
import android.util.AttributeSet;
-import android.widget.LinearLayout;
+import android.util.Slog;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+public class LatestItemView extends FrameLayout {
-public class NotificationLinearLayout extends LinearLayout {
- public NotificationLinearLayout(Context context, AttributeSet attrs) {
+ public LatestItemView(Context context, AttributeSet attrs) {
super(context, attrs);
}
-}
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ return onTouchEvent(ev);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
new file mode 100644
index 0000000..7a82267
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.app.Notification;
+import android.os.IBinder;
+import android.view.View;
+
+import com.android.internal.statusbar.StatusBarNotification;
+
+import java.util.ArrayList;
+
+/**
+ * The list of currently displaying notifications.
+ */
+public class NotificationData {
+ public static final class Entry {
+ public IBinder key;
+ public StatusBarNotification notification;
+ public StatusBarIconView icon;
+ public View row; // the outer expanded view
+ public View content; // takes the click events and sends the PendingIntent
+ public View expanded; // the inflated RemoteViews
+ }
+ private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
+
+ public int size() {
+ return mEntries.size();
+ }
+
+ public Entry getEntryAt(int index) {
+ return mEntries.get(index);
+ }
+
+ public int findEntry(IBinder key) {
+ final int N = mEntries.size();
+ for (int i=0; i<N; i++) {
+ Entry entry = mEntries.get(i);
+ if (entry.key == key) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public int add(IBinder key, StatusBarNotification notification, View row, View content,
+ View expanded, StatusBarIconView icon) {
+ Entry entry = new Entry();
+ entry.key = key;
+ entry.notification = notification;
+ entry.row = row;
+ entry.content = content;
+ entry.expanded = expanded;
+ entry.icon = icon;
+ final int index = chooseIndex(notification.notification.when);
+ mEntries.add(index, entry);
+ return index;
+ }
+
+ public Entry remove(IBinder key) {
+ final int N = mEntries.size();
+ for (int i=0; i<N; i++) {
+ Entry entry = mEntries.get(i);
+ if (entry.key == key) {
+ mEntries.remove(i);
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ private int chooseIndex(final long when) {
+ final int N = mEntries.size();
+ for (int i=0; i<N; i++) {
+ Entry entry = mEntries.get(i);
+ if (entry.notification.notification.when > when) {
+ return i;
+ }
+ }
+ return N;
+ }
+
+ /**
+ * Return whether there are any visible items (i.e. items without an error).
+ */
+ public boolean hasVisibleItems() {
+ final int N = mEntries.size();
+ for (int i=0; i<N; i++) {
+ Entry entry = mEntries.get(i);
+ if (entry.expanded != null) { // the view successfully inflated
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return whether there are any clearable items (that aren't errors).
+ */
+ public boolean hasClearableItems() {
+ final int N = mEntries.size();
+ for (int i=0; i<N; i++) {
+ Entry entry = mEntries.get(i);
+ if (entry.expanded != null) { // the view successfully inflated
+ if ((entry.notification.notification.flags & Notification.FLAG_NO_CLEAR) == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/services/java/com/android/server/status/NotificationLinearLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLinearLayout.java
similarity index 95%
rename from services/java/com/android/server/status/NotificationLinearLayout.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/NotificationLinearLayout.java
index 2fdf956..8105352 100644
--- a/services/java/com/android/server/status/NotificationLinearLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLinearLayout.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
new file mode 100644
index 0000000..e9ae69a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -0,0 +1,1419 @@
+/*
+ * Copyright (C) 2010 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.systemui.statusbar;
+
+import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarNotification;
+
+import android.app.ActivityManagerNative;
+import android.app.Dialog;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.FrameLayout;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Set;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.StatusBarPolicy;
+
+public class PhoneStatusBarService extends StatusBarService {
+ static final String TAG = "PhoneStatusBarService";
+ static final boolean SPEW = false;
+
+ public static final String ACTION_STATUSBAR_START
+ = "com.android.internal.policy.statusbar.START";
+
+ static final int EXPANDED_LEAVE_ALONE = -10000;
+ static final int EXPANDED_FULL_OPEN = -10001;
+
+ private static final int MSG_ANIMATE = 1000;
+ private static final int MSG_ANIMATE_REVEAL = 1001;
+
+ public interface NotificationCallbacks {
+ void onSetDisabled(int status);
+ void onClearAll();
+ void onNotificationClick(String pkg, String tag, int id);
+ void onPanelRevealed();
+ }
+
+ private class ExpandedDialog extends Dialog {
+ ExpandedDialog(Context context) {
+ super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_BACK:
+ if (!down) {
+ animateCollapse();
+ }
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+ }
+
+ StatusBarPolicy mIconPolicy;
+
+ int mHeight;
+ int mIconWidth;
+
+ Display mDisplay;
+ StatusBarView mStatusBarView;
+ int mPixelFormat;
+ H mHandler = new H();
+ Object mQueueLock = new Object();
+
+ // icons
+ String[] mRightIconSlots;
+ LinearLayout mIcons;
+ IconMerger mNotificationIcons;
+ LinearLayout mStatusIcons;
+
+ // expanded notifications
+ Dialog mExpandedDialog;
+ ExpandedView mExpandedView;
+ WindowManager.LayoutParams mExpandedParams;
+ ScrollView mScrollView;
+ View mNotificationLinearLayout;
+ View mExpandedContents;
+ // top bar
+ TextView mNoNotificationsTitle;
+ TextView mClearButton;
+ // drag bar
+ CloseDragHandle mCloseView;
+ // ongoing
+ NotificationData mOngoing = new NotificationData();
+ TextView mOngoingTitle;
+ LinearLayout mOngoingItems;
+ // latest
+ NotificationData mLatest = new NotificationData();
+ TextView mLatestTitle;
+ LinearLayout mLatestItems;
+ // position
+ int[] mPositionTmp = new int[2];
+ boolean mExpanded;
+ boolean mExpandedVisible;
+
+ // the date view
+ DateView mDateView;
+
+ // the tracker view
+ TrackingView mTrackingView;
+ WindowManager.LayoutParams mTrackingParams;
+ int mTrackingPosition; // the position of the top of the tracking view.
+
+ // ticker
+ private Ticker mTicker;
+ private View mTickerView;
+ private boolean mTicking;
+
+ // Tracking finger for opening/closing.
+ int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
+ boolean mTracking;
+ VelocityTracker mVelocityTracker;
+
+ static final int ANIM_FRAME_DURATION = (1000/60);
+
+ boolean mAnimating;
+ long mCurAnimationTime;
+ float mDisplayHeight;
+ float mAnimY;
+ float mAnimVel;
+ float mAnimAccel;
+ long mAnimLastTime;
+ boolean mAnimatingReveal = false;
+ int mViewDelta;
+ int[] mAbsPos = new int[2];
+
+ // for disabling the status bar
+ int mDisabled = 0;
+
+ /**
+ * Construct the service, add the status bar view to the window manager
+ */
+ @Override
+ public void onCreate() {
+ // First set up our views and stuff.
+ mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+ makeStatusBarView(this);
+
+ // Next, call super.onCreate(), which will populate our views.
+ super.onCreate();
+
+ // Lastly, call to the icon policy to install/update all the icons.
+ mIconPolicy = new StatusBarPolicy(this);
+ }
+
+ // ================================================================================
+ // Constructing the view
+ // ================================================================================
+ private void makeStatusBarView(Context context) {
+ Resources res = context.getResources();
+ mRightIconSlots = res.getStringArray(R.array.status_bar_icon_order);
+
+ mHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+ mIconWidth = mHeight;
+
+ ExpandedView expanded = (ExpandedView)View.inflate(context,
+ R.layout.status_bar_expanded, null);
+ expanded.mService = this;
+ StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
+ sb.mService = this;
+
+ // figure out which pixel-format to use for the status bar.
+ mPixelFormat = PixelFormat.TRANSLUCENT;
+ Drawable bg = sb.getBackground();
+ if (bg != null) {
+ mPixelFormat = bg.getOpacity();
+ }
+
+ mStatusBarView = sb;
+ mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
+ mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
+ mIcons = (LinearLayout)sb.findViewById(R.id.icons);
+ mTickerView = sb.findViewById(R.id.ticker);
+ mDateView = (DateView)sb.findViewById(R.id.date);
+
+ mExpandedDialog = new ExpandedDialog(context);
+ mExpandedView = expanded;
+ mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
+ mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
+ mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
+ mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
+ mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
+ mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
+ mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
+ mClearButton.setOnClickListener(mClearButtonListener);
+ mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
+ mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
+
+ mOngoingTitle.setVisibility(View.GONE);
+ mLatestTitle.setVisibility(View.GONE);
+
+ mTicker = new MyTicker(context, sb);
+
+ TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
+ tickerView.mTicker = mTicker;
+
+ mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null);
+ mTrackingView.mService = this;
+ mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
+ mCloseView.mService = this;
+
+ mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
+ // the more notifications icon
+ StatusBarIconView moreView = new StatusBarIconView(this, "more");
+ moreView.set(new StatusBarIcon(null, R.drawable.stat_notify_more, 0));
+ mNotificationIcons.addMoreView(moreView,
+ new LinearLayout.LayoutParams(mIconWidth, mHeight));
+
+ // set the inital view visibility
+ setAreThereNotifications();
+ mDateView.setVisibility(View.INVISIBLE);
+
+ // receive broadcasts
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ context.registerReceiver(mBroadcastReceiver, filter);
+ }
+
+ @Override
+ protected void addStatusBarView() {
+ final StatusBarView view = mStatusBarView;
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ mHeight,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
+ PixelFormat.RGBX_8888);
+ lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ lp.setTitle("StatusBar");
+ // TODO lp.windowAnimations = R.style.Animation_StatusBar;
+
+ WindowManagerImpl.getDefault().addView(view, lp);
+ }
+
+ public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
+ Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ + " icon=" + icon);
+ StatusBarIconView view = new StatusBarIconView(this, slot);
+ view.set(icon);
+ mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconWidth, mHeight));
+ }
+
+ public void updateIcon(String slot, int index, int viewIndex,
+ StatusBarIcon old, StatusBarIcon icon) {
+ Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ + " old=" + old + " icon=" + icon);
+ StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
+ view.set(icon);
+ }
+
+ public void removeIcon(String slot, int index, int viewIndex) {
+ Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
+ mStatusIcons.removeViewAt(viewIndex);
+ }
+
+ public void addNotification(IBinder key, StatusBarNotification notification) {
+ addNotificationViews(key, notification);
+
+ // show the ticker
+ tick(notification);
+
+ // Recalculate the position of the sliding windows and the titles.
+ setAreThereNotifications();
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ }
+
+ public void updateNotification(IBinder key, StatusBarNotification notification) {
+ Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification);
+
+ NotificationData oldList;
+ int oldIndex = mOngoing.findEntry(key);
+ if (oldIndex >= 0) {
+ oldList = mOngoing;
+ } else {
+ oldIndex = mLatest.findEntry(key);
+ if (oldIndex < 0) {
+ Slog.w(TAG, "updateNotification for unknown key: " + key);
+ return;
+ }
+ oldList = mLatest;
+ }
+ final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex);
+ final StatusBarNotification oldNotification = oldEntry.notification;
+ final RemoteViews oldContentView = oldNotification.notification.contentView;
+
+ final RemoteViews contentView = notification.notification.contentView;
+
+ // Can we just reapply the RemoteViews in place? If when didn't change, the order
+ // didn't change.
+ if (notification.notification.when == oldNotification.notification.when
+ && notification.isOngoing() == oldNotification.isOngoing()
+ && oldEntry.expanded != null
+ && contentView != null && oldContentView != null
+ && contentView.getPackage() != null
+ && oldContentView.getPackage() != null
+ && oldContentView.getPackage().equals(contentView.getPackage())
+ && oldContentView.getLayoutId() == contentView.getLayoutId()) {
+ Slog.d(TAG, "reusing notification");
+ oldEntry.notification = notification;
+ try {
+ // Reapply the RemoteViews
+ contentView.reapply(this, oldEntry.content);
+ // update the contentIntent
+ final PendingIntent contentIntent = notification.notification.contentIntent;
+ if (contentIntent != null) {
+ oldEntry.content.setOnClickListener(new Launcher(contentIntent,
+ notification.pkg, notification.tag, notification.id));
+ }
+ }
+ catch (RuntimeException e) {
+ // It failed to add cleanly. Log, and remove the view from the panel.
+ Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
+ removeNotificationViews(key);
+ addNotificationViews(key, notification);
+ }
+ // Update the icon.
+ oldEntry.icon.set(new StatusBarIcon(notification.pkg, notification.notification.icon,
+ notification.notification.iconLevel, notification.notification.number));
+ } else {
+ Slog.d(TAG, "not reusing notification");
+ removeNotificationViews(key);
+ addNotificationViews(key, notification);
+ }
+
+ // Restart the ticker if it's still running
+ tick(notification);
+
+ // Recalculate the position of the sliding windows and the titles.
+ setAreThereNotifications();
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ }
+
+ public void removeNotification(IBinder key) {
+ Slog.d(TAG, "removeNotification key=" + key);
+ StatusBarNotification old = removeNotificationViews(key);
+
+ if (old != null) {
+ // Cancel the ticker if it's still running
+ mTicker.removeEntry(old);
+
+ // Recalculate the position of the sliding windows and the titles.
+ setAreThereNotifications();
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ }
+ }
+
+ private int chooseIconIndex(boolean isOngoing, int viewIndex) {
+ final int latestSize = mLatest.size();
+ if (isOngoing) {
+ return latestSize + (mOngoing.size() - viewIndex);
+ } else {
+ return latestSize - viewIndex;
+ }
+ }
+
+ View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
+ Notification n = notification.notification;
+ RemoteViews remoteViews = n.contentView;
+ if (remoteViews == null) {
+ return null;
+ }
+
+ // create the row view
+ LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View row = inflater.inflate(com.android.internal.R.layout.status_bar_latest_event,
+ parent, false);
+
+ // bind the click event to the content area
+ ViewGroup content = (ViewGroup)row.findViewById(com.android.internal.R.id.content);
+ content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ content.setOnFocusChangeListener(mFocusChangeListener);
+ PendingIntent contentIntent = n.contentIntent;
+ if (contentIntent != null) {
+ content.setOnClickListener(new Launcher(contentIntent, notification.pkg,
+ notification.tag, notification.id));
+ }
+
+ View expanded = null;
+ Exception exception = null;
+ try {
+ expanded = remoteViews.apply(this, content);
+ }
+ catch (RuntimeException e) {
+ exception = e;
+ }
+ if (expanded == null) {
+ Slog.e(TAG, "couldn't inflate view for package " + notification.pkg, exception);
+ row.setVisibility(View.GONE);
+ } else {
+ content.addView(expanded);
+ row.setDrawingCacheEnabled(true);
+ }
+
+ return new View[] { row, content, expanded };
+ }
+
+ void addNotificationViews(IBinder key, StatusBarNotification notification) {
+ NotificationData list;
+ ViewGroup parent;
+ final boolean isOngoing = notification.isOngoing();
+ if (isOngoing) {
+ list = mOngoing;
+ parent = mOngoingItems;
+ } else {
+ list = mLatest;
+ parent = mLatestItems;
+ }
+ // Construct the expanded view.
+ final View[] views = makeNotificationView(notification, parent);
+ final View row = views[0];
+ final View content = views[1];
+ final View expanded = views[2];
+ // Construct the icon.
+ StatusBarIconView iconView = new StatusBarIconView(this,
+ notification.pkg + "/0x" + Integer.toHexString(notification.id));
+ iconView.set(new StatusBarIcon(notification.pkg, notification.notification.icon,
+ notification.notification.iconLevel, notification.notification.number));
+ // Add the expanded view.
+ final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
+ parent.addView(row, viewIndex);
+ // Add the icon.
+ final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
+ mNotificationIcons.addView(iconView, iconIndex,
+ new LinearLayout.LayoutParams(mIconWidth, mHeight));
+ }
+
+ StatusBarNotification removeNotificationViews(IBinder key) {
+ NotificationData.Entry entry = mOngoing.remove(key);
+ if (entry == null) {
+ entry = mLatest.remove(key);
+ if (entry == null) {
+ Slog.w(TAG, "removeNotification for unknown key: " + key);
+ return null;
+ }
+ }
+ // Remove the expanded view.
+ ((ViewGroup)entry.row.getParent()).removeView(entry.row);
+ // Remove the icon.
+ ((ViewGroup)entry.icon.getParent()).removeView(entry.icon);
+
+ return entry.notification;
+ }
+
+ private void setAreThereNotifications() {
+ boolean ongoing = mOngoing.hasVisibleItems();
+ boolean latest = mLatest.hasVisibleItems();
+
+ // (no ongoing notifications are clearable)
+ if (mLatest.hasClearableItems()) {
+ mClearButton.setVisibility(View.VISIBLE);
+ } else {
+ mClearButton.setVisibility(View.INVISIBLE);
+ }
+
+ mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
+ mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
+
+ if (ongoing || latest) {
+ mNoNotificationsTitle.setVisibility(View.GONE);
+ } else {
+ mNoNotificationsTitle.setVisibility(View.VISIBLE);
+ }
+ }
+
+
+ /**
+ * State is one or more of the DISABLE constants from StatusBarManager.
+ */
+ public void disable(int state) {
+ final int old = mDisabled;
+ final int diff = state ^ old;
+ mDisabled = state;
+
+ if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+ if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
+ Slog.d(TAG, "DISABLE_EXPAND: yes");
+ animateCollapse();
+ }
+ }
+ if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+ if (mTicking) {
+ mTicker.halt();
+ } else {
+ setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
+ mTicker.halt();
+ }
+ }
+ }
+
+ /**
+ * All changes to the status bar and notifications funnel through here and are batched.
+ */
+ private class H extends Handler {
+ public void handleMessage(Message m) {
+ switch (m.what) {
+ case MSG_ANIMATE:
+ doAnimation();
+ break;
+ case MSG_ANIMATE_REVEAL:
+ doRevealAnimation();
+ break;
+ }
+ }
+ }
+
+ View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
+ public void onFocusChange(View v, boolean hasFocus) {
+ // Because 'v' is a ViewGroup, all its children will be (un)selected
+ // too, which allows marqueeing to work.
+ v.setSelected(hasFocus);
+ }
+ };
+
+ private void makeExpandedVisible() {
+ if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
+ if (mExpandedVisible) {
+ return;
+ }
+ mExpandedVisible = true;
+ visibilityChanged(true);
+
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ mExpandedView.requestFocus(View.FOCUS_FORWARD);
+ mTrackingView.setVisibility(View.VISIBLE);
+
+ if (!mTicking) {
+ setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+
+ public void animateExpand() {
+ if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return ;
+ }
+ if (mExpanded) {
+ return;
+ }
+
+ prepareTracking(0, true);
+ performFling(0, 2000.0f, true);
+ }
+
+ public void animateCollapse() {
+ if (SPEW) {
+ Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
+ + " mExpandedVisible=" + mExpandedVisible
+ + " mExpanded=" + mExpanded
+ + " mAnimating=" + mAnimating
+ + " mAnimY=" + mAnimY
+ + " mAnimVel=" + mAnimVel);
+ }
+
+ if (!mExpandedVisible) {
+ return;
+ }
+
+ int y;
+ if (mAnimating) {
+ y = (int)mAnimY;
+ } else {
+ y = mDisplay.getHeight()-1;
+ }
+ // Let the fling think that we're open so it goes in the right direction
+ // and doesn't try to re-open the windowshade.
+ mExpanded = true;
+ prepareTracking(y, false);
+ performFling(y, -2000.0f, true);
+ }
+
+ void performExpand() {
+ if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return ;
+ }
+ if (mExpanded) {
+ return;
+ }
+
+ mExpanded = true;
+ makeExpandedVisible();
+ updateExpandedViewPos(EXPANDED_FULL_OPEN);
+
+ if (false) postStartTracing();
+ }
+
+ void performCollapse() {
+ if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
+ + " mExpandedVisible=" + mExpandedVisible);
+
+ if (!mExpandedVisible) {
+ return;
+ }
+ mExpandedVisible = false;
+ visibilityChanged(false);
+ mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ mTrackingView.setVisibility(View.GONE);
+
+ if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
+ setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
+
+ if (!mExpanded) {
+ return;
+ }
+ mExpanded = false;
+ }
+
+ void doAnimation() {
+ if (mAnimating) {
+ if (SPEW) Slog.d(TAG, "doAnimation");
+ if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
+ incrementAnim();
+ if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY);
+ if (mAnimY >= mDisplay.getHeight()-1) {
+ if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
+ mAnimating = false;
+ updateExpandedViewPos(EXPANDED_FULL_OPEN);
+ performExpand();
+ }
+ else if (mAnimY < mStatusBarView.getHeight()) {
+ if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
+ mAnimating = false;
+ updateExpandedViewPos(0);
+ performCollapse();
+ }
+ else {
+ updateExpandedViewPos((int)mAnimY);
+ mCurAnimationTime += ANIM_FRAME_DURATION;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+ }
+ }
+ }
+
+ void stopTracking() {
+ mTracking = false;
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+
+ void incrementAnim() {
+ long now = SystemClock.uptimeMillis();
+ float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s
+ final float y = mAnimY;
+ final float v = mAnimVel; // px/s
+ final float a = mAnimAccel; // px/s/s
+ mAnimY = y + (v*t) + (0.5f*a*t*t); // px
+ mAnimVel = v + (a*t); // px/s
+ mAnimLastTime = now; // ms
+ //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
+ // + " mAnimAccel=" + mAnimAccel);
+ }
+
+ void doRevealAnimation() {
+ final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
+ if (mAnimatingReveal && mAnimating && mAnimY < h) {
+ incrementAnim();
+ if (mAnimY >= h) {
+ mAnimY = h;
+ updateExpandedViewPos((int)mAnimY);
+ } else {
+ updateExpandedViewPos((int)mAnimY);
+ mCurAnimationTime += ANIM_FRAME_DURATION;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+ mCurAnimationTime);
+ }
+ }
+ }
+
+ void prepareTracking(int y, boolean opening) {
+ mTracking = true;
+ mVelocityTracker = VelocityTracker.obtain();
+ if (opening) {
+ mAnimAccel = 2000.0f;
+ mAnimVel = 200;
+ mAnimY = mStatusBarView.getHeight();
+ updateExpandedViewPos((int)mAnimY);
+ mAnimating = true;
+ mAnimatingReveal = true;
+ mHandler.removeMessages(MSG_ANIMATE);
+ mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+ long now = SystemClock.uptimeMillis();
+ mAnimLastTime = now;
+ mCurAnimationTime = now + ANIM_FRAME_DURATION;
+ mAnimating = true;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+ mCurAnimationTime);
+ makeExpandedVisible();
+ } else {
+ // it's open, close it?
+ if (mAnimating) {
+ mAnimating = false;
+ mHandler.removeMessages(MSG_ANIMATE);
+ }
+ updateExpandedViewPos(y + mViewDelta);
+ }
+ }
+
+ void performFling(int y, float vel, boolean always) {
+ mAnimatingReveal = false;
+ mDisplayHeight = mDisplay.getHeight();
+
+ mAnimY = y;
+ mAnimVel = vel;
+
+ //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
+
+ if (mExpanded) {
+ if (!always && (
+ vel > 200.0f
+ || (y > (mDisplayHeight-25) && vel > -200.0f))) {
+ // We are expanded, but they didn't move sufficiently to cause
+ // us to retract. Animate back to the expanded position.
+ mAnimAccel = 2000.0f;
+ if (vel < 0) {
+ mAnimVel = 0;
+ }
+ }
+ else {
+ // We are expanded and are now going to animate away.
+ mAnimAccel = -2000.0f;
+ if (vel > 0) {
+ mAnimVel = 0;
+ }
+ }
+ } else {
+ if (always || (
+ vel > 200.0f
+ || (y > (mDisplayHeight/2) && vel > -200.0f))) {
+ // We are collapsed, and they moved enough to allow us to
+ // expand. Animate in the notifications.
+ mAnimAccel = 2000.0f;
+ if (vel < 0) {
+ mAnimVel = 0;
+ }
+ }
+ else {
+ // We are collapsed, but they didn't move sufficiently to cause
+ // us to retract. Animate back to the collapsed position.
+ mAnimAccel = -2000.0f;
+ if (vel > 0) {
+ mAnimVel = 0;
+ }
+ }
+ }
+ //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
+ // + " mAnimAccel=" + mAnimAccel);
+
+ long now = SystemClock.uptimeMillis();
+ mAnimLastTime = now;
+ mCurAnimationTime = now + ANIM_FRAME_DURATION;
+ mAnimating = true;
+ mHandler.removeMessages(MSG_ANIMATE);
+ mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+ stopTracking();
+ }
+
+ boolean interceptTouchEvent(MotionEvent event) {
+ if (SPEW) {
+ Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
+ + mDisabled);
+ }
+
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return false;
+ }
+
+ final int statusBarSize = mStatusBarView.getHeight();
+ final int hitSize = statusBarSize*2;
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ final int y = (int)event.getRawY();
+
+ if (!mExpanded) {
+ mViewDelta = statusBarSize - y;
+ } else {
+ mTrackingView.getLocationOnScreen(mAbsPos);
+ mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
+ }
+ if ((!mExpanded && y < hitSize) ||
+ (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
+
+ // We drop events at the edge of the screen to make the windowshade come
+ // down by accident less, especially when pushing open a device with a keyboard
+ // that rotates (like g1 and droid)
+ int x = (int)event.getRawX();
+ final int edgeBorder = mEdgeBorder;
+ if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
+ prepareTracking(y, !mExpanded);// opening if we're not already fully visible
+ mVelocityTracker.addMovement(event);
+ }
+ }
+ } else if (mTracking) {
+ mVelocityTracker.addMovement(event);
+ final int minY = statusBarSize + mCloseView.getHeight();
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ int y = (int)event.getRawY();
+ if (mAnimatingReveal && y < minY) {
+ // nothing
+ } else {
+ mAnimatingReveal = false;
+ updateExpandedViewPos(y + mViewDelta);
+ }
+ } else if (event.getAction() == MotionEvent.ACTION_UP) {
+ mVelocityTracker.computeCurrentVelocity(1000);
+
+ float yVel = mVelocityTracker.getYVelocity();
+ boolean negative = yVel < 0;
+
+ float xVel = mVelocityTracker.getXVelocity();
+ if (xVel < 0) {
+ xVel = -xVel;
+ }
+ if (xVel > 150.0f) {
+ xVel = 150.0f; // limit how much we care about the x axis
+ }
+
+ float vel = (float)Math.hypot(yVel, xVel);
+ if (negative) {
+ vel = -vel;
+ }
+
+ performFling((int)event.getRawY(), vel, false);
+ }
+
+ }
+ return false;
+ }
+
+ private class Launcher implements View.OnClickListener {
+ private PendingIntent mIntent;
+ private String mPkg;
+ private String mTag;
+ private int mId;
+
+ Launcher(PendingIntent intent, String pkg, String tag, int id) {
+ mIntent = intent;
+ mPkg = pkg;
+ mTag = tag;
+ mId = id;
+ }
+
+ public void onClick(View v) {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManagerNative.getDefault().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ Intent overlay = new Intent();
+ overlay.setSourceBounds(
+ new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+ try {
+ mIntent.send(PhoneStatusBarService.this, 0, overlay);
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here. Just log the exception message.
+ Slog.w(TAG, "Sending contentIntent failed: " + e);
+ }
+ try {
+ mBarService.onNotificationClick(mPkg, mTag, mId);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ animateCollapse();
+ }
+ }
+
+ private void tick(StatusBarNotification n) {
+ // Show the ticker if one is requested. Also don't do this
+ // until status bar window is attached to the window manager,
+ // because... well, what's the point otherwise? And trying to
+ // run a ticker without being attached will crash!
+ if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
+ if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
+ | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
+ mTicker.addEntry(n);
+ }
+ }
+ }
+
+ private class MyTicker extends Ticker {
+ MyTicker(Context context, StatusBarView sb) {
+ super(context, sb);
+ }
+
+ @Override
+ void tickerStarting() {
+ mTicking = true;
+ mIcons.setVisibility(View.GONE);
+ mTickerView.setVisibility(View.VISIBLE);
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
+ if (mExpandedVisible) {
+ setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
+ }
+ }
+
+ @Override
+ void tickerDone() {
+ mIcons.setVisibility(View.VISIBLE);
+ mTickerView.setVisibility(View.GONE);
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
+ mTickingDoneListener));
+ if (mExpandedVisible) {
+ setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
+ }
+ }
+
+ void tickerHalting() {
+ mIcons.setVisibility(View.VISIBLE);
+ mTickerView.setVisibility(View.GONE);
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
+ mTickingDoneListener));
+ if (mExpandedVisible) {
+ setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+ }
+
+ Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
+ public void onAnimationEnd(Animation animation) {
+ mTicking = false;
+ }
+ public void onAnimationRepeat(Animation animation) {
+ }
+ public void onAnimationStart(Animation animation) {
+ }
+ };
+
+ private Animation loadAnim(int id, Animation.AnimationListener listener) {
+ Animation anim = AnimationUtils.loadAnimation(PhoneStatusBarService.this, id);
+ if (listener != null) {
+ anim.setAnimationListener(listener);
+ }
+ return anim;
+ }
+
+ public String viewInfo(View v) {
+ return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
+ + " " + v.getWidth() + "x" + v.getHeight() + ")";
+ }
+
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump StatusBar from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mQueueLock) {
+ pw.println("Current Status Bar state:");
+ pw.println(" mExpanded=" + mExpanded
+ + ", mExpandedVisible=" + mExpandedVisible);
+ pw.println(" mTicking=" + mTicking);
+ pw.println(" mTracking=" + mTracking);
+ pw.println(" mAnimating=" + mAnimating
+ + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
+ + ", mAnimAccel=" + mAnimAccel);
+ pw.println(" mCurAnimationTime=" + mCurAnimationTime
+ + " mAnimLastTime=" + mAnimLastTime);
+ pw.println(" mDisplayHeight=" + mDisplayHeight
+ + " mAnimatingReveal=" + mAnimatingReveal
+ + " mViewDelta=" + mViewDelta);
+ pw.println(" mDisplayHeight=" + mDisplayHeight);
+ pw.println(" mExpandedParams: " + mExpandedParams);
+ pw.println(" mExpandedView: " + viewInfo(mExpandedView));
+ pw.println(" mExpandedDialog: " + mExpandedDialog);
+ pw.println(" mTrackingParams: " + mTrackingParams);
+ pw.println(" mTrackingView: " + viewInfo(mTrackingView));
+ pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle));
+ pw.println(" mOngoingItems: " + viewInfo(mOngoingItems));
+ pw.println(" mLatestTitle: " + viewInfo(mLatestTitle));
+ pw.println(" mLatestItems: " + viewInfo(mLatestItems));
+ pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
+ pw.println(" mCloseView: " + viewInfo(mCloseView));
+ pw.println(" mTickerView: " + viewInfo(mTickerView));
+ pw.println(" mScrollView: " + viewInfo(mScrollView)
+ + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
+ pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
+ }
+ /*
+ synchronized (mNotificationData) {
+ int N = mNotificationData.ongoingCount();
+ pw.println(" ongoingCount.size=" + N);
+ for (int i=0; i<N; i++) {
+ StatusBarNotification n = mNotificationData.getOngoing(i);
+ pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
+ pw.println(" data=" + n.data);
+ }
+ N = mNotificationData.latestCount();
+ pw.println(" ongoingCount.size=" + N);
+ for (int i=0; i<N; i++) {
+ StatusBarNotification n = mNotificationData.getLatest(i);
+ pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
+ pw.println(" data=" + n.data);
+ }
+ }
+ */
+
+ if (false) {
+ pw.println("see the logcat for a dump of the views we have created.");
+ // must happen on ui thread
+ mHandler.post(new Runnable() {
+ public void run() {
+ mStatusBarView.getLocationOnScreen(mAbsPos);
+ Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+ + ") " + mStatusBarView.getWidth() + "x"
+ + mStatusBarView.getHeight());
+ mStatusBarView.debug();
+
+ mExpandedView.getLocationOnScreen(mAbsPos);
+ Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+ + ") " + mExpandedView.getWidth() + "x"
+ + mExpandedView.getHeight());
+ mExpandedView.debug();
+
+ mTrackingView.getLocationOnScreen(mAbsPos);
+ Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+ + ") " + mTrackingView.getWidth() + "x"
+ + mTrackingView.getHeight());
+ mTrackingView.debug();
+ }
+ });
+ }
+ }
+
+ void onBarViewAttached() {
+ WindowManager.LayoutParams lp;
+ int pixelFormat;
+ Drawable bg;
+
+ /// ---------- Tracking View --------------
+ pixelFormat = PixelFormat.RGBX_8888;
+ bg = mTrackingView.getBackground();
+ if (bg != null) {
+ pixelFormat = bg.getOpacity();
+ }
+
+ lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ pixelFormat);
+// lp.token = mStatusBarView.getWindowToken();
+ lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ lp.setTitle("TrackingView");
+ lp.y = mTrackingPosition;
+ mTrackingParams = lp;
+
+ WindowManagerImpl.getDefault().addView(mTrackingView, lp);
+ }
+
+ void onTrackingViewAttached() {
+ WindowManager.LayoutParams lp;
+ int pixelFormat;
+ Drawable bg;
+
+ /// ---------- Expanded View --------------
+ pixelFormat = PixelFormat.TRANSLUCENT;
+
+ final int disph = mDisplay.getHeight();
+ lp = mExpandedDialog.getWindow().getAttributes();
+ lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ lp.height = getExpandedHeight();
+ lp.x = 0;
+ mTrackingPosition = lp.y = -disph; // sufficiently large negative
+ lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+ lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_DITHER
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ lp.format = pixelFormat;
+ lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ lp.setTitle("StatusBarExpanded");
+ mExpandedDialog.getWindow().setAttributes(lp);
+ mExpandedDialog.getWindow().setFormat(pixelFormat);
+ mExpandedParams = lp;
+
+ mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+ mExpandedDialog.setContentView(mExpandedView,
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ mExpandedDialog.getWindow().setBackgroundDrawable(null);
+ mExpandedDialog.show();
+ FrameLayout hack = (FrameLayout)mExpandedView.getParent();
+ }
+
+ void setDateViewVisibility(boolean visible, int anim) {
+ mDateView.setUpdates(visible);
+ mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ mDateView.startAnimation(loadAnim(anim, null));
+ }
+
+ void setNotificationIconVisibility(boolean visible, int anim) {
+ int old = mNotificationIcons.getVisibility();
+ int v = visible ? View.VISIBLE : View.INVISIBLE;
+ if (old != v) {
+ mNotificationIcons.setVisibility(v);
+ mNotificationIcons.startAnimation(loadAnim(anim, null));
+ }
+ }
+
+ void updateExpandedViewPos(int expandedPosition) {
+ if (SPEW) {
+ Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
+ + " mTrackingParams.y=" + mTrackingParams.y
+ + " mTrackingPosition=" + mTrackingPosition);
+ }
+
+ int h = mStatusBarView.getHeight();
+ int disph = mDisplay.getHeight();
+
+ // If the expanded view is not visible, make sure they're still off screen.
+ // Maybe the view was resized.
+ if (!mExpandedVisible) {
+ if (mTrackingView != null) {
+ mTrackingPosition = -disph;
+ if (mTrackingParams != null) {
+ mTrackingParams.y = mTrackingPosition;
+ WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+ }
+ }
+ if (mExpandedParams != null) {
+ mExpandedParams.y = -disph;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ }
+ return;
+ }
+
+ // tracking view...
+ int pos;
+ if (expandedPosition == EXPANDED_FULL_OPEN) {
+ pos = h;
+ }
+ else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
+ pos = mTrackingPosition;
+ }
+ else {
+ if (expandedPosition <= disph) {
+ pos = expandedPosition;
+ } else {
+ pos = disph;
+ }
+ pos -= disph-h;
+ }
+ mTrackingPosition = mTrackingParams.y = pos;
+ mTrackingParams.height = disph-h;
+ WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+
+ if (mExpandedParams != null) {
+ mCloseView.getLocationInWindow(mPositionTmp);
+ final int closePos = mPositionTmp[1];
+
+ mExpandedContents.getLocationInWindow(mPositionTmp);
+ final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
+
+ mExpandedParams.y = pos + mTrackingView.getHeight()
+ - (mTrackingParams.height-closePos) - contentsBottom;
+ int max = h;
+ if (mExpandedParams.y > max) {
+ mExpandedParams.y = max;
+ }
+ int min = mTrackingPosition;
+ if (mExpandedParams.y < min) {
+ mExpandedParams.y = min;
+ }
+
+ boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
+ if (!visible) {
+ // if the contents aren't visible, move the expanded view way off screen
+ // because the window itself extends below the content view.
+ mExpandedParams.y = -disph;
+ }
+ visibilityChanged(visible);
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ }
+
+ if (SPEW) {
+ Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition
+ + " mTrackingParams.y=" + mTrackingParams.y
+ + " mTrackingPosition=" + mTrackingPosition
+ + " mExpandedParams.y=" + mExpandedParams.y
+ + " mExpandedParams.height=" + mExpandedParams.height);
+ }
+ }
+
+ int getExpandedHeight() {
+ return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
+ }
+
+ void updateExpandedHeight() {
+ if (mExpandedView != null) {
+ mExpandedParams.height = getExpandedHeight();
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ }
+ }
+
+ /**
+ * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
+ * This was added last-minute and is inconsistent with the way the rest of the notifications
+ * are handled, because the notification isn't really cancelled. The lights are just
+ * turned off. If any other notifications happen, the lights will turn back on. Steve says
+ * this is what he wants. (see bug 1131461)
+ */
+ private boolean mPanelSlightlyVisible;
+ void visibilityChanged(boolean visible) {
+ if (mPanelSlightlyVisible != visible) {
+ mPanelSlightlyVisible = visible;
+ try {
+ mBarService.visibilityChanged(visible);
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
+ }
+ }
+ }
+
+ void performDisableActions(int net) {
+ int old = mDisabled;
+ int diff = net ^ old;
+ mDisabled = net;
+
+ // act accordingly
+ if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+ if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
+ Slog.d(TAG, "DISABLE_EXPAND: yes");
+ animateCollapse();
+ }
+ }
+ if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+ if (mTicking) {
+ mNotificationIcons.setVisibility(View.INVISIBLE);
+ mTicker.halt();
+ } else {
+ setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ mTicker.halt();
+ }
+ }
+ }
+
+ private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ try {
+ mBarService.onClearAllNotifications();
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ animateCollapse();
+ }
+ };
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+ || Intent.ACTION_SCREEN_OFF.equals(action)) {
+ //collapse();
+ }
+ else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+ updateResources();
+ }
+ }
+ };
+
+ /**
+ * Reload some of our resources when the configuration changes.
+ *
+ * We don't reload everything when the configuration changes -- we probably
+ * should, but getting that smooth is tough. Someday we'll fix that. In the
+ * meantime, just update the things that we know change.
+ */
+ void updateResources() {
+ Resources res = getResources();
+
+ mClearButton.setText(getText(R.string.status_bar_clear_all_button));
+ mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title));
+ mLatestTitle.setText(getText(R.string.status_bar_latest_events_title));
+ mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title));
+
+ mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
+ if (false) Slog.v(TAG, "updateResources");
+ }
+
+ //
+ // tracing
+ //
+
+ void postStartTracing() {
+ mHandler.postDelayed(mStartTracing, 3000);
+ }
+
+ void vibrate() {
+ android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
+ vib.vibrate(250);
+ }
+
+ Runnable mStartTracing = new Runnable() {
+ public void run() {
+ vibrate();
+ SystemClock.sleep(250);
+ Slog.d(TAG, "startTracing");
+ android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
+ mHandler.postDelayed(mStopTracing, 10000);
+ }
+ };
+
+ Runnable mStopTracing = new Runnable() {
+ public void run() {
+ android.os.Debug.stopMethodTracing();
+ Slog.d(TAG, "stopTracing");
+ vibrate();
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
new file mode 100644
index 0000000..5eb0d68
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.Canvas;
+import android.util.Slog;
+import android.view.ViewDebug;
+import android.widget.FrameLayout;
+
+import com.android.internal.statusbar.StatusBarIcon;
+
+public class StatusBarIconView extends AnimatedImageView {
+ private static final String TAG = "StatusBarIconView";
+
+ private StatusBarIcon mIcon;
+ @ViewDebug.ExportedProperty private String mSlot;
+ @ViewDebug.ExportedProperty private boolean mError;
+
+ public StatusBarIconView(Context context, String slot) {
+ super(context);
+ mSlot = slot;
+ }
+
+ private static boolean streq(String a, String b) {
+ if (a == b) {
+ return true;
+ }
+ if (a == null && b != null) {
+ return false;
+ }
+ if (a != null && b == null) {
+ return false;
+ }
+ return a.equals(b);
+ }
+
+ public void set(StatusBarIcon icon) {
+ error: {
+ final boolean iconEquals = !mError
+ && mIcon != null
+ && streq(mIcon.iconPackage, icon.iconPackage)
+ && mIcon.iconId == icon.iconId;
+ final boolean levelEquals = !mError
+ && iconEquals
+ && mIcon.iconLevel == icon.iconLevel;
+ final boolean visibilityEquals = !mError
+ && mIcon != null
+ && mIcon.visible == icon.visible;
+ mError = false;
+ if (!iconEquals) {
+ Drawable drawable = getIcon(icon);
+ if (drawable == null) {
+ mError = true;
+ Slog.w(PhoneStatusBarService.TAG, "No icon ID for slot " + mSlot);
+ break error;
+ }
+ setImageDrawable(drawable);
+ }
+ if (!levelEquals) {
+ setImageLevel(icon.iconLevel);
+ }
+ if (!visibilityEquals) {
+ setVisibility(icon.visible ? VISIBLE : GONE);
+ }
+ mIcon = icon.clone();
+ }
+ if (mError) {
+ setVisibility(GONE);
+ }
+ }
+
+ private Drawable getIcon(StatusBarIcon icon) {
+ return getIcon(getContext(), icon);
+ }
+
+ /**
+ * Returns the right icon to use for this item, respecting the iconId and
+ * iconPackage (if set)
+ *
+ * @param context Context to use to get resources if iconPackage is not set
+ * @return Drawable for this item, or null if the package or item could not
+ * be found
+ */
+ public static Drawable getIcon(Context context, StatusBarIcon icon) {
+ Resources r = null;
+
+ if (icon.iconPackage != null) {
+ try {
+ r = context.getPackageManager().getResourcesForApplication(icon.iconPackage);
+ } catch (PackageManager.NameNotFoundException ex) {
+ Slog.e(PhoneStatusBarService.TAG, "Icon package not found: "+icon.iconPackage, ex);
+ return null;
+ }
+ } else {
+ r = context.getResources();
+ }
+
+ if (icon.iconId == 0) {
+ return null;
+ }
+
+ try {
+ return r.getDrawable(icon.iconId);
+ } catch (RuntimeException e) {
+ Slog.w(PhoneStatusBarService.TAG, "Icon not found in "
+ + (icon.iconPackage != null ? icon.iconId : "<system>")
+ + ": " + Integer.toHexString(icon.iconId));
+ }
+
+ return null;
+ }
+
+ public StatusBarIcon getStatusBarIcon() {
+ return mIcon;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
new file mode 100644
index 0000000..9ef9d0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.StatusBarManager;
+import android.app.AlertDialog;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothPbap;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.TypedArray;
+import android.graphics.PixelFormat;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.location.LocationManager;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.text.format.DateFormat;
+import android.text.style.CharacterStyle;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.util.Slog;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.cdma.EriInfo;
+import com.android.internal.telephony.cdma.TtyIntent;
+import com.android.server.am.BatteryStatsService;
+
+import com.android.systemui.R;
+
+/**
+ * This class contains all of the policy about which icons are installed in the status
+ * bar at boot time. It goes through the normal API for icons, even though it probably
+ * strictly doesn't need to.
+ */
+public class StatusBarPolicy {
+ private static final String TAG = "StatusBarPolicy";
+
+ // message codes for the handler
+ private static final int EVENT_BATTERY_CLOSE = 4;
+
+ private static final int AM_PM_STYLE_NORMAL = 0;
+ private static final int AM_PM_STYLE_SMALL = 1;
+ private static final int AM_PM_STYLE_GONE = 2;
+
+ private static final int AM_PM_STYLE = AM_PM_STYLE_GONE;
+
+ private final Context mContext;
+ private final StatusBarManager mService;
+ private final Handler mHandler = new StatusBarHandler();
+ private final IBatteryStats mBatteryStats;
+
+ // storage
+ private StorageManager mStorageManager;
+
+ // battery
+ private boolean mBatteryFirst = true;
+ private boolean mBatteryPlugged;
+ private int mBatteryLevel;
+ private AlertDialog mLowBatteryDialog;
+ private TextView mBatteryLevelTextView;
+ private View mBatteryView;
+ private int mBatteryViewSequence;
+ private boolean mBatteryShowLowOnEndCall = false;
+ private static final boolean SHOW_LOW_BATTERY_WARNING = true;
+ private static final boolean SHOW_BATTERY_WARNINGS_IN_CALL = true;
+
+ // phone
+ private TelephonyManager mPhone;
+ private int mPhoneSignalIconId;
+
+ //***** Signal strength icons
+ //GSM/UMTS
+ private static final int[] sSignalImages = new int[] {
+ R.drawable.stat_sys_signal_0,
+ R.drawable.stat_sys_signal_1,
+ R.drawable.stat_sys_signal_2,
+ R.drawable.stat_sys_signal_3,
+ R.drawable.stat_sys_signal_4
+ };
+ private static final int[] sSignalImages_r = new int[] {
+ R.drawable.stat_sys_r_signal_0,
+ R.drawable.stat_sys_r_signal_1,
+ R.drawable.stat_sys_r_signal_2,
+ R.drawable.stat_sys_r_signal_3,
+ R.drawable.stat_sys_r_signal_4
+ };
+ private static final int[] sRoamingIndicatorImages_cdma = new int[] {
+ R.drawable.stat_sys_roaming_cdma_0, //Standard Roaming Indicator
+ // 1 is Standard Roaming Indicator OFF
+ // TODO T: image never used, remove and put 0 instead?
+ R.drawable.stat_sys_roaming_cdma_0,
+
+ // 2 is Standard Roaming Indicator FLASHING
+ // TODO T: image never used, remove and put 0 instead?
+ R.drawable.stat_sys_roaming_cdma_0,
+
+ // 3-12 Standard ERI
+ R.drawable.stat_sys_roaming_cdma_0, //3
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+
+ // 13-63 Reserved for Standard ERI
+ R.drawable.stat_sys_roaming_cdma_0, //13
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+
+ // 64-127 Reserved for Non Standard (Operator Specific) ERI
+ R.drawable.stat_sys_roaming_cdma_0, //64
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0,
+ R.drawable.stat_sys_roaming_cdma_0 //83
+
+ // 128-255 Reserved
+ };
+
+ //***** Data connection icons
+ private int[] mDataIconList = sDataNetType_g;
+ //GSM/UMTS
+ private static final int[] sDataNetType_g = new int[] {
+ R.drawable.stat_sys_data_connected_g,
+ R.drawable.stat_sys_data_in_g,
+ R.drawable.stat_sys_data_out_g,
+ R.drawable.stat_sys_data_inandout_g,
+ };
+ private static final int[] sDataNetType_3g = new int[] {
+ R.drawable.stat_sys_data_connected_3g,
+ R.drawable.stat_sys_data_in_3g,
+ R.drawable.stat_sys_data_out_3g,
+ R.drawable.stat_sys_data_inandout_3g,
+ };
+ private static final int[] sDataNetType_e = new int[] {
+ R.drawable.stat_sys_data_connected_e,
+ R.drawable.stat_sys_data_in_e,
+ R.drawable.stat_sys_data_out_e,
+ R.drawable.stat_sys_data_inandout_e,
+ };
+ //3.5G
+ private static final int[] sDataNetType_h = new int[] {
+ R.drawable.stat_sys_data_connected_h,
+ R.drawable.stat_sys_data_in_h,
+ R.drawable.stat_sys_data_out_h,
+ R.drawable.stat_sys_data_inandout_h,
+ };
+
+ //CDMA
+ // Use 3G icons for EVDO data and 1x icons for 1XRTT data
+ private static final int[] sDataNetType_1x = new int[] {
+ R.drawable.stat_sys_data_connected_1x,
+ R.drawable.stat_sys_data_in_1x,
+ R.drawable.stat_sys_data_out_1x,
+ R.drawable.stat_sys_data_inandout_1x,
+ };
+
+ // Assume it's all good unless we hear otherwise. We don't always seem
+ // to get broadcasts that it *is* there.
+ IccCard.State mSimState = IccCard.State.READY;
+ int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
+ int mDataState = TelephonyManager.DATA_DISCONNECTED;
+ int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
+ ServiceState mServiceState;
+ SignalStrength mSignalStrength;
+
+ // data connection
+ private boolean mDataIconVisible;
+ private boolean mHspaDataDistinguishable;
+
+ // ringer volume
+ private boolean mVolumeVisible;
+
+ // bluetooth device status
+ private int mBluetoothHeadsetState;
+ private boolean mBluetoothA2dpConnected;
+ private int mBluetoothPbapState;
+ private boolean mBluetoothEnabled;
+
+ // wifi
+ private static final int[] sWifiSignalImages = new int[] {
+ R.drawable.stat_sys_wifi_signal_1,
+ R.drawable.stat_sys_wifi_signal_2,
+ R.drawable.stat_sys_wifi_signal_3,
+ R.drawable.stat_sys_wifi_signal_4,
+ };
+ private static final int sWifiTemporarilyNotConnectedImage =
+ R.drawable.stat_sys_wifi_signal_0;
+
+ private int mLastWifiSignalLevel = -1;
+ private boolean mIsWifiConnected = false;
+
+ // sync state
+ // If sync is active the SyncActive icon is displayed. If sync is not active but
+ // sync is failing the SyncFailing icon is displayed. Otherwise neither are displayed.
+
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+ updateBattery(intent);
+ }
+ else if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
+ updateAlarm(intent);
+ }
+ else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
+ updateSyncState(intent);
+ }
+ else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
+ onBatteryLow(intent);
+ }
+ else if (action.equals(Intent.ACTION_BATTERY_OKAY)
+ || action.equals(Intent.ACTION_POWER_CONNECTED)) {
+ onBatteryOkay(intent);
+ }
+ else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
+ action.equals(BluetoothHeadset.ACTION_STATE_CHANGED) ||
+ action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED) ||
+ action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
+ updateBluetooth(intent);
+ }
+ else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) ||
+ action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) ||
+ action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+ updateWifi(intent);
+ }
+ else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) ||
+ action.equals(LocationManager.GPS_FIX_CHANGE_ACTION)) {
+ updateGps(intent);
+ }
+ else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
+ action.equals(AudioManager.VIBRATE_SETTING_CHANGED_ACTION)) {
+ updateVolume();
+ }
+ else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+ updateSimState(intent);
+ }
+ else if (action.equals(TtyIntent.TTY_ENABLED_CHANGE_ACTION)) {
+ updateTTY(intent);
+ }
+ }
+ };
+
+ public StatusBarPolicy(Context context) {
+ mContext = context;
+ mService = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
+ mSignalStrength = new SignalStrength();
+ mBatteryStats = BatteryStatsService.getService();
+
+ // storage
+ mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+ mStorageManager.registerListener(
+ new com.android.server.status.StorageNotification(context));
+
+ // battery
+ mService.setIcon("battery", com.android.internal.R.drawable.stat_sys_battery_unknown, 0);
+
+ // phone_signal
+ mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+ mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
+ mService.setIcon("phone_signal", mPhoneSignalIconId, 0);
+
+ // register for phone state notifications.
+ ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE))
+ .listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_SERVICE_STATE
+ | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
+ | PhoneStateListener.LISTEN_CALL_STATE
+ | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+ | PhoneStateListener.LISTEN_DATA_ACTIVITY);
+
+ // data_connection
+ mService.setIcon("data_connection", R.drawable.stat_sys_data_connected_g, 0);
+ mService.setIconVisibility("data_connection", false);
+
+ // wifi
+ mService.setIcon("wifi", sWifiSignalImages[0], 0);
+ mService.setIconVisibility("wifi", false);
+ // wifi will get updated by the sticky intents
+
+ // TTY status
+ mService.setIcon("tty", R.drawable.stat_sys_tty_mode, 0);
+ mService.setIconVisibility("tty", false);
+
+ // Cdma Roaming Indicator, ERI
+ mService.setIcon("cdma_eri", R.drawable.stat_sys_roaming_cdma_0, 0);
+ mService.setIconVisibility("cdma_eri", false);
+
+ // bluetooth status
+ mService.setIcon("bluetooth", R.drawable.stat_sys_data_bluetooth, 0);
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ mBluetoothEnabled = adapter.isEnabled();
+ } else {
+ mBluetoothEnabled = false;
+ }
+ mBluetoothA2dpConnected = false;
+ mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
+ mBluetoothPbapState = BluetoothPbap.STATE_DISCONNECTED;
+ mService.setIconVisibility("bluetooth", mBluetoothEnabled);
+
+ // Gps status
+ mService.setIcon("gps", R.drawable.stat_sys_gps_acquiring_anim, 0);
+ mService.setIconVisibility("gps", false);
+
+ // Alarm clock
+ mService.setIcon("alarm_clock", R.drawable.stat_notify_alarm, 0);
+ mService.setIconVisibility("alarm_clock", false);
+
+ // Sync state
+ mService.setIcon("sync_active", com.android.internal.R.drawable.stat_notify_sync_anim0, 0);
+ mService.setIcon("sync_failing", com.android.internal.R.drawable.stat_notify_sync_error, 0);
+ mService.setIconVisibility("sync_active", false);
+ mService.setIconVisibility("sync_failing", false);
+
+ // volume
+ mService.setIcon("volume", R.drawable.stat_sys_ringer_silent, 0);
+ mService.setIconVisibility("volume", false);
+ updateVolume();
+
+ IntentFilter filter = new IntentFilter();
+
+ // Register for Intent broadcasts for...
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_BATTERY_LOW);
+ filter.addAction(Intent.ACTION_BATTERY_OKAY);
+ filter.addAction(Intent.ACTION_POWER_CONNECTED);
+ filter.addAction(Intent.ACTION_ALARM_CHANGED);
+ filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
+ filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+ filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+ filter.addAction(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+ filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+ filter.addAction(LocationManager.GPS_FIX_CHANGE_ACTION);
+ filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
+ mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
+
+ // load config to determine if to distinguish Hspa data icon
+ try {
+ mHspaDataDistinguishable = mContext.getResources().getBoolean(
+ R.bool.config_hspa_data_distinguishable);
+ } catch (Exception e) {
+ mHspaDataDistinguishable = false;
+ }
+ }
+
+ private final void updateAlarm(Intent intent) {
+ boolean alarmSet = intent.getBooleanExtra("alarmSet", false);
+ mService.setIconVisibility("alarm_clock", alarmSet);
+ }
+
+ private final void updateSyncState(Intent intent) {
+ boolean isActive = intent.getBooleanExtra("active", false);
+ boolean isFailing = intent.getBooleanExtra("failing", false);
+ mService.setIconVisibility("sync_active", isActive);
+ // Don't display sync failing icon: BUG 1297963 Set sync error timeout to "never"
+ //mService.setIconVisibility("sync_failing", isFailing && !isActive);
+ }
+
+ private final void updateBattery(Intent intent) {
+ final int id = intent.getIntExtra("icon-small", 0);
+ int level = intent.getIntExtra("level", 0);
+ mService.setIcon("battery", id, level);
+
+ boolean plugged = intent.getIntExtra("plugged", 0) != 0;
+ level = intent.getIntExtra("level", -1);
+ if (false) {
+ Slog.d(TAG, "updateBattery level=" + level
+ + " plugged=" + plugged
+ + " mBatteryPlugged=" + mBatteryPlugged
+ + " mBatteryLevel=" + mBatteryLevel
+ + " mBatteryFirst=" + mBatteryFirst);
+ }
+
+ boolean oldPlugged = mBatteryPlugged;
+
+ mBatteryPlugged = plugged;
+ mBatteryLevel = level;
+
+ if (mBatteryFirst) {
+ mBatteryFirst = false;
+ }
+ /*
+ * No longer showing the battery view because it draws attention away
+ * from the USB storage notification. We could still show it when
+ * connected to a brick, but that could lead to the user into thinking
+ * the device does not charge when plugged into USB (since he/she would
+ * not see the same battery screen on USB as he sees on brick).
+ */
+ if (false) {
+ Slog.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
+ }
+ }
+
+ private void onBatteryLow(Intent intent) {
+ if (SHOW_LOW_BATTERY_WARNING) {
+ if (false) {
+ Slog.d(TAG, "mPhoneState=" + mPhoneState
+ + " mLowBatteryDialog=" + mLowBatteryDialog
+ + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
+ }
+
+ if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
+ showLowBatteryWarning();
+ } else {
+ mBatteryShowLowOnEndCall = true;
+ }
+ }
+ }
+
+ private void onBatteryOkay(Intent intent) {
+ if (mLowBatteryDialog != null
+ && SHOW_LOW_BATTERY_WARNING) {
+ mLowBatteryDialog.dismiss();
+ mBatteryShowLowOnEndCall = false;
+ }
+ }
+
+ private void setBatteryLevel(View parent, int id, int height, int background, int level) {
+ ImageView v = (ImageView)parent.findViewById(id);
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)v.getLayoutParams();
+ lp.weight = height;
+ if (background != 0) {
+ v.setBackgroundResource(background);
+ Drawable bkg = v.getBackground();
+ bkg.setLevel(level);
+ }
+ }
+
+ private void showLowBatteryWarning() {
+ closeLastBatteryView();
+
+ // Show exact battery level.
+ CharSequence levelText = mContext.getString(
+ R.string.battery_low_percent_format, mBatteryLevel);
+
+ if (mBatteryLevelTextView != null) {
+ mBatteryLevelTextView.setText(levelText);
+ } else {
+ View v = View.inflate(mContext, R.layout.battery_low, null);
+ mBatteryLevelTextView=(TextView)v.findViewById(R.id.level_percent);
+
+ mBatteryLevelTextView.setText(levelText);
+
+ AlertDialog.Builder b = new AlertDialog.Builder(mContext);
+ b.setCancelable(true);
+ b.setTitle(R.string.battery_low_title);
+ b.setView(v);
+ b.setIcon(android.R.drawable.ic_dialog_alert);
+ b.setPositiveButton(android.R.string.ok, null);
+
+ final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_NO_HISTORY);
+ if (intent.resolveActivity(mContext.getPackageManager()) != null) {
+ b.setNegativeButton(R.string.battery_low_why,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mContext.startActivity(intent);
+ if (mLowBatteryDialog != null) {
+ mLowBatteryDialog.dismiss();
+ }
+ }
+ });
+ }
+
+ AlertDialog d = b.create();
+ d.setOnDismissListener(mLowBatteryListener);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ d.show();
+ mLowBatteryDialog = d;
+ }
+
+ final ContentResolver cr = mContext.getContentResolver();
+ if (Settings.System.getInt(cr,
+ Settings.System.POWER_SOUNDS_ENABLED, 1) == 1)
+ {
+ final String soundPath = Settings.System.getString(cr,
+ Settings.System.LOW_BATTERY_SOUND);
+ if (soundPath != null) {
+ final Uri soundUri = Uri.parse("file://" + soundPath);
+ if (soundUri != null) {
+ final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+ if (sfx != null) {
+ sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+ sfx.play();
+ }
+ }
+ }
+ }
+ }
+
+ private final void updateCallState(int state) {
+ mPhoneState = state;
+ if (false) {
+ Slog.d(TAG, "mPhoneState=" + mPhoneState
+ + " mLowBatteryDialog=" + mLowBatteryDialog
+ + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
+ }
+ if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
+ if (mBatteryShowLowOnEndCall) {
+ if (!mBatteryPlugged) {
+ showLowBatteryWarning();
+ }
+ mBatteryShowLowOnEndCall = false;
+ }
+ } else {
+ if (mLowBatteryDialog != null) {
+ mLowBatteryDialog.dismiss();
+ mBatteryShowLowOnEndCall = true;
+ }
+ }
+ }
+
+ private DialogInterface.OnDismissListener mLowBatteryListener
+ = new DialogInterface.OnDismissListener() {
+ public void onDismiss(DialogInterface dialog) {
+ mLowBatteryDialog = null;
+ mBatteryLevelTextView = null;
+ }
+ };
+
+ private void scheduleCloseBatteryView() {
+ Message m = mHandler.obtainMessage(EVENT_BATTERY_CLOSE);
+ m.arg1 = (++mBatteryViewSequence);
+ mHandler.sendMessageDelayed(m, 3000);
+ }
+
+ private void closeLastBatteryView() {
+ if (mBatteryView != null) {
+ //mBatteryView.debug();
+ WindowManagerImpl.getDefault().removeView(mBatteryView);
+ mBatteryView = null;
+ }
+ }
+
+ private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ mSignalStrength = signalStrength;
+ updateSignalStrength();
+ }
+
+ @Override
+ public void onServiceStateChanged(ServiceState state) {
+ mServiceState = state;
+ updateSignalStrength();
+ updateCdmaRoamingIcon(state);
+ updateDataIcon();
+ }
+
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ updateCallState(state);
+ // In cdma, if a voice call is made, RSSI should switch to 1x.
+ if (isCdma()) {
+ updateSignalStrength();
+ }
+ }
+
+ @Override
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ mDataState = state;
+ updateDataNetType(networkType);
+ updateDataIcon();
+ }
+
+ @Override
+ public void onDataActivity(int direction) {
+ mDataActivity = direction;
+ updateDataIcon();
+ }
+ };
+
+ private final void updateSimState(Intent intent) {
+ String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
+ if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+ mSimState = IccCard.State.ABSENT;
+ }
+ else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+ mSimState = IccCard.State.READY;
+ }
+ else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+ final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
+ if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+ mSimState = IccCard.State.PIN_REQUIRED;
+ }
+ else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+ mSimState = IccCard.State.PUK_REQUIRED;
+ }
+ else {
+ mSimState = IccCard.State.NETWORK_LOCKED;
+ }
+ } else {
+ mSimState = IccCard.State.UNKNOWN;
+ }
+ updateDataIcon();
+ }
+
+ private boolean isCdma() {
+ return (mSignalStrength != null) && !mSignalStrength.isGsm();
+ }
+
+ private boolean isEvdo() {
+ return ( (mServiceState != null)
+ && ((mServiceState.getRadioTechnology()
+ == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
+ || (mServiceState.getRadioTechnology()
+ == ServiceState.RADIO_TECHNOLOGY_EVDO_A)
+ || (mServiceState.getRadioTechnology()
+ == ServiceState.RADIO_TECHNOLOGY_EVDO_B)));
+ }
+
+ private boolean hasService() {
+ if (mServiceState != null) {
+ switch (mServiceState.getState()) {
+ case ServiceState.STATE_OUT_OF_SERVICE:
+ case ServiceState.STATE_POWER_OFF:
+ return false;
+ default:
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private final void updateSignalStrength() {
+ int iconLevel = -1;
+ int[] iconList;
+
+ // Display signal strength while in "emergency calls only" mode
+ if (!hasService() && !mServiceState.isEmergencyOnly()) {
+ //Slog.d(TAG, "updateSignalStrength: no service");
+ if (Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
+ mPhoneSignalIconId = R.drawable.stat_sys_signal_flightmode;
+ } else {
+ mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
+ }
+ mService.setIcon("phone_signal", mPhoneSignalIconId, 0);
+ return;
+ }
+
+ if (!isCdma()) {
+ int asu = mSignalStrength.getGsmSignalStrength();
+
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ if (asu <= 2 || asu == 99) iconLevel = 0;
+ else if (asu >= 12) iconLevel = 4;
+ else if (asu >= 8) iconLevel = 3;
+ else if (asu >= 5) iconLevel = 2;
+ else iconLevel = 1;
+
+ // Though mPhone is a Manager, this call is not an IPC
+ if (mPhone.isNetworkRoaming()) {
+ iconList = sSignalImages_r;
+ } else {
+ iconList = sSignalImages;
+ }
+ } else {
+ iconList = this.sSignalImages;
+
+ // If 3G(EV) and 1x network are available than 3G should be
+ // displayed, displayed RSSI should be from the EV side.
+ // If a voice call is made then RSSI should switch to 1x.
+ if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
+ iconLevel = getEvdoLevel();
+ if (false) {
+ Slog.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level=" + getCdmaLevel());
+ }
+ } else {
+ iconLevel = getCdmaLevel();
+ }
+ }
+ mPhoneSignalIconId = iconList[iconLevel];
+ mService.setIcon("phone_signal", mPhoneSignalIconId, 0);
+ }
+
+ private int getCdmaLevel() {
+ final int cdmaDbm = mSignalStrength.getCdmaDbm();
+ final int cdmaEcio = mSignalStrength.getCdmaEcio();
+ int levelDbm = 0;
+ int levelEcio = 0;
+
+ if (cdmaDbm >= -75) levelDbm = 4;
+ else if (cdmaDbm >= -85) levelDbm = 3;
+ else if (cdmaDbm >= -95) levelDbm = 2;
+ else if (cdmaDbm >= -100) levelDbm = 1;
+ else levelDbm = 0;
+
+ // Ec/Io are in dB*10
+ if (cdmaEcio >= -90) levelEcio = 4;
+ else if (cdmaEcio >= -110) levelEcio = 3;
+ else if (cdmaEcio >= -130) levelEcio = 2;
+ else if (cdmaEcio >= -150) levelEcio = 1;
+ else levelEcio = 0;
+
+ return (levelDbm < levelEcio) ? levelDbm : levelEcio;
+ }
+
+ private int getEvdoLevel() {
+ int evdoDbm = mSignalStrength.getEvdoDbm();
+ int evdoSnr = mSignalStrength.getEvdoSnr();
+ int levelEvdoDbm = 0;
+ int levelEvdoSnr = 0;
+
+ if (evdoDbm >= -65) levelEvdoDbm = 4;
+ else if (evdoDbm >= -75) levelEvdoDbm = 3;
+ else if (evdoDbm >= -90) levelEvdoDbm = 2;
+ else if (evdoDbm >= -105) levelEvdoDbm = 1;
+ else levelEvdoDbm = 0;
+
+ if (evdoSnr >= 7) levelEvdoSnr = 4;
+ else if (evdoSnr >= 5) levelEvdoSnr = 3;
+ else if (evdoSnr >= 3) levelEvdoSnr = 2;
+ else if (evdoSnr >= 1) levelEvdoSnr = 1;
+ else levelEvdoSnr = 0;
+
+ return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+ }
+
+ private final void updateDataNetType(int net) {
+ switch (net) {
+ case TelephonyManager.NETWORK_TYPE_EDGE:
+ mDataIconList = sDataNetType_e;
+ break;
+ case TelephonyManager.NETWORK_TYPE_UMTS:
+ mDataIconList = sDataNetType_3g;
+ break;
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ if (mHspaDataDistinguishable) {
+ mDataIconList = sDataNetType_h;
+ } else {
+ mDataIconList = sDataNetType_3g;
+ }
+ break;
+ case TelephonyManager.NETWORK_TYPE_CDMA:
+ // display 1xRTT for IS95A/B
+ mDataIconList = this.sDataNetType_1x;
+ break;
+ case TelephonyManager.NETWORK_TYPE_1xRTT:
+ mDataIconList = this.sDataNetType_1x;
+ break;
+ case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
+ case TelephonyManager.NETWORK_TYPE_EVDO_A:
+ case TelephonyManager.NETWORK_TYPE_EVDO_B:
+ mDataIconList = sDataNetType_3g;
+ break;
+ default:
+ mDataIconList = sDataNetType_g;
+ break;
+ }
+ }
+
+ private final void updateDataIcon() {
+ int iconId;
+ boolean visible = true;
+
+ if (!isCdma()) {
+ // GSM case, we have to check also the sim state
+ if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) {
+ if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
+ switch (mDataActivity) {
+ case TelephonyManager.DATA_ACTIVITY_IN:
+ iconId = mDataIconList[1];
+ break;
+ case TelephonyManager.DATA_ACTIVITY_OUT:
+ iconId = mDataIconList[2];
+ break;
+ case TelephonyManager.DATA_ACTIVITY_INOUT:
+ iconId = mDataIconList[3];
+ break;
+ default:
+ iconId = mDataIconList[0];
+ break;
+ }
+ mService.setIcon("data_connection", iconId, 0);
+ } else {
+ visible = false;
+ }
+ } else {
+ iconId = R.drawable.stat_sys_no_sim;
+ mService.setIcon("data_connection", iconId, 0);
+ }
+ } else {
+ // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT
+ if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
+ switch (mDataActivity) {
+ case TelephonyManager.DATA_ACTIVITY_IN:
+ iconId = mDataIconList[1];
+ break;
+ case TelephonyManager.DATA_ACTIVITY_OUT:
+ iconId = mDataIconList[2];
+ break;
+ case TelephonyManager.DATA_ACTIVITY_INOUT:
+ iconId = mDataIconList[3];
+ break;
+ case TelephonyManager.DATA_ACTIVITY_DORMANT:
+ default:
+ iconId = mDataIconList[0];
+ break;
+ }
+ mService.setIcon("data_connection", iconId, 0);
+ } else {
+ visible = false;
+ }
+ }
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ if (mDataIconVisible != visible) {
+ mService.setIconVisibility("data_connection", visible);
+ mDataIconVisible = visible;
+ }
+ }
+
+ private final void updateVolume() {
+ AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ final int ringerMode = audioManager.getRingerMode();
+ final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
+ ringerMode == AudioManager.RINGER_MODE_VIBRATE;
+ final int iconId = audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)
+ ? R.drawable.stat_sys_ringer_vibrate
+ : R.drawable.stat_sys_ringer_silent;
+
+ if (visible) {
+ mService.setIcon("volume", iconId, 0);
+ }
+ if (visible != mVolumeVisible) {
+ mService.setIconVisibility("volume", visible);
+ mVolumeVisible = visible;
+ }
+ }
+
+ private final void updateBluetooth(Intent intent) {
+ int iconId = R.drawable.stat_sys_data_bluetooth;
+ String action = intent.getAction();
+ if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ mBluetoothEnabled = state == BluetoothAdapter.STATE_ON;
+ } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
+ mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
+ BluetoothHeadset.STATE_ERROR);
+ } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
+ BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
+ if (a2dp.getConnectedSinks().size() != 0) {
+ mBluetoothA2dpConnected = true;
+ } else {
+ mBluetoothA2dpConnected = false;
+ }
+ } else if (action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
+ mBluetoothPbapState = intent.getIntExtra(BluetoothPbap.PBAP_STATE,
+ BluetoothPbap.STATE_DISCONNECTED);
+ } else {
+ return;
+ }
+
+ if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || mBluetoothA2dpConnected ||
+ mBluetoothPbapState == BluetoothPbap.STATE_CONNECTED) {
+ iconId = R.drawable.stat_sys_data_bluetooth_connected;
+ }
+
+ mService.setIcon("bluetooth", iconId, 0);
+ mService.setIconVisibility("bluetooth", mBluetoothEnabled);
+ }
+
+ private final void updateWifi(Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+
+ final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
+
+ if (!enabled) {
+ // If disabled, hide the icon. (We show icon when connected.)
+ mService.setIconVisibility("wifi", false);
+ }
+
+ } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
+ final boolean enabled = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED,
+ false);
+ if (!enabled) {
+ mService.setIconVisibility("wifi", false);
+ }
+ } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+
+ final NetworkInfo networkInfo = (NetworkInfo)
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+
+ int iconId;
+ if (networkInfo != null && networkInfo.isConnected()) {
+ mIsWifiConnected = true;
+ if (mLastWifiSignalLevel == -1) {
+ iconId = sWifiSignalImages[0];
+ } else {
+ iconId = sWifiSignalImages[mLastWifiSignalLevel];
+ }
+
+ // Show the icon since wi-fi is connected
+ mService.setIconVisibility("wifi", true);
+
+ } else {
+ mLastWifiSignalLevel = -1;
+ mIsWifiConnected = false;
+ iconId = sWifiSignalImages[0];
+
+ // Hide the icon since we're not connected
+ mService.setIconVisibility("wifi", false);
+ }
+
+ mService.setIcon("wifi", iconId, 0);
+ } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+ int iconId;
+ final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
+ int newSignalLevel = WifiManager.calculateSignalLevel(newRssi,
+ sWifiSignalImages.length);
+ if (newSignalLevel != mLastWifiSignalLevel) {
+ mLastWifiSignalLevel = newSignalLevel;
+ if (mIsWifiConnected) {
+ iconId = sWifiSignalImages[newSignalLevel];
+ } else {
+ iconId = sWifiTemporarilyNotConnectedImage;
+ }
+ mService.setIcon("wifi", iconId, 0);
+ }
+ }
+ }
+
+ private final void updateGps(Intent intent) {
+ final String action = intent.getAction();
+ final boolean enabled = intent.getBooleanExtra(LocationManager.EXTRA_GPS_ENABLED, false);
+
+ if (action.equals(LocationManager.GPS_FIX_CHANGE_ACTION) && enabled) {
+ // GPS is getting fixes
+ mService.setIcon("gps", com.android.internal.R.drawable.stat_sys_gps_on, 0);
+ mService.setIconVisibility("gps", true);
+ } else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
+ // GPS is off
+ mService.setIconVisibility("gps", false);
+ } else {
+ // GPS is on, but not receiving fixes
+ mService.setIcon("gps", R.drawable.stat_sys_gps_acquiring_anim, 0);
+ mService.setIconVisibility("gps", true);
+ }
+ }
+
+ private final void updateTTY(Intent intent) {
+ final String action = intent.getAction();
+ final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
+
+ if (false) Slog.v(TAG, "updateTTY: enabled: " + enabled);
+
+ if (enabled) {
+ // TTY is on
+ if (false) Slog.v(TAG, "updateTTY: set TTY on");
+ mService.setIcon("tty", R.drawable.stat_sys_tty_mode, 0);
+ mService.setIconVisibility("tty", true);
+ } else {
+ // TTY is off
+ if (false) Slog.v(TAG, "updateTTY: set TTY off");
+ mService.setIconVisibility("tty", false);
+ }
+ }
+
+ private final void updateCdmaRoamingIcon(ServiceState state) {
+ if (!hasService()) {
+ mService.setIconVisibility("cdma_eri", false);
+ return;
+ }
+
+ if (!isCdma()) {
+ mService.setIconVisibility("cdma_eri", false);
+ return;
+ }
+
+ int[] iconList = sRoamingIndicatorImages_cdma;
+ int iconIndex = state.getCdmaEriIconIndex();
+ int iconMode = state.getCdmaEriIconMode();
+
+ if (iconIndex == -1) {
+ Slog.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update");
+ return;
+ }
+
+ if (iconMode == -1) {
+ Slog.e(TAG, "getCdmeEriIconMode returned null, skipping ERI icon update");
+ return;
+ }
+
+ if (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) {
+ if (false) Slog.v(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon");
+ mService.setIconVisibility("cdma_eri", false);
+ return;
+ }
+
+ switch (iconMode) {
+ case EriInfo.ROAMING_ICON_MODE_NORMAL:
+ mService.setIcon("cdma_eri", iconList[iconIndex], 0);
+ mService.setIconVisibility("cdma_eri", true);
+ break;
+ case EriInfo.ROAMING_ICON_MODE_FLASH:
+ mService.setIcon("cdma_eri", R.drawable.stat_sys_roaming_cdma_flash, 0);
+ mService.setIconVisibility("cdma_eri", true);
+ break;
+
+ }
+ mService.setIcon("phone_signal", mPhoneSignalIconId, 0);
+ }
+
+
+ private class StatusBarHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_BATTERY_CLOSE:
+ if (msg.arg1 == mBatteryViewSequence) {
+ closeLastBatteryView();
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
new file mode 100644
index 0000000..4d9e695
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 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.systemui.statusbar;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+
+import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIconList;
+import com.android.internal.statusbar.StatusBarNotification;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+
+public abstract class StatusBarService extends Service implements CommandQueue.Callbacks {
+ private static final String TAG = "StatusBarService";
+
+ CommandQueue mCommandQueue;
+ IStatusBarService mBarService;
+
+ /* TODO
+ H mHandler = new H();
+ Object mQueueLock = new Object();
+ NotificationCallbacks mNotificationCallbacks;
+ */
+
+ @Override
+ public void onCreate() {
+ // Connect in to the status bar manager service
+ StatusBarIconList iconList = new StatusBarIconList();
+ ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
+ ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
+ mCommandQueue = new CommandQueue(this, iconList);
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ try {
+ mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications);
+ } catch (RemoteException ex) {
+ // If the system process isn't there we're doomed anyway.
+ }
+
+ // Set up the initial icon state
+ int N = iconList.size();
+ int viewIndex = 0;
+ for (int i=0; i<N; i++) {
+ StatusBarIcon icon = iconList.getIcon(i);
+ if (icon != null) {
+ addIcon(iconList.getSlot(i), i, viewIndex, icon);
+ viewIndex++;
+ }
+ }
+
+ // Set up the initial notification state
+ N = notificationKeys.size();
+ if (N == notifications.size()) {
+ for (int i=0; i<N; i++) {
+ addNotification(notificationKeys.get(i), notifications.get(i));
+ }
+ } else {
+ Log.wtf(TAG, "Notification list length mismatch: keys=" + N
+ + " notifications=" + notifications.size());
+ }
+
+ // Put up the view
+ addStatusBarView();
+ }
+
+ @Override
+ public void onDestroy() {
+ // we're never destroyed
+ }
+
+ /**
+ * Nobody binds to us.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ /**
+ * Implement this to add the main status bar view.
+ */
+ protected abstract void addStatusBarView();
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStarter.java
new file mode 100644
index 0000000..2b9dfb0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStarter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 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.systemui.statusbar;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.util.Log;
+
+/**
+ * Receive a broadcast from the StatusBarManagerService at boot time, and
+ * kick off the StatusBarService.
+ */
+public class StatusBarStarter extends BroadcastReceiver {
+ private static final String TAG = "StatusBarStarter";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "StatusBarStarter onReceive intent=" + intent);
+ context.startService(new Intent(context, PhoneStatusBarService.class));
+ }
+}
+
+
diff --git a/services/java/com/android/server/status/StatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
similarity index 96%
rename from services/java/com/android/server/status/StatusBarView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
index 5e1f572..466cc75 100644
--- a/services/java/com/android/server/status/StatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.content.Context;
import android.content.res.Configuration;
@@ -27,14 +27,14 @@
import android.view.ViewParent;
import android.widget.FrameLayout;
-import com.android.internal.R;
+import com.android.systemui.R;
public class StatusBarView extends FrameLayout {
private static final String TAG = "StatusBarView";
static final int DIM_ANIM_TIME = 400;
- StatusBarService mService;
+ PhoneStatusBarService mService;
boolean mTracking;
int mStartX, mStartY;
ViewGroup mNotificationIcons;
@@ -94,7 +94,7 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
+ mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
}
@Override
diff --git a/services/java/com/android/server/status/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
similarity index 79%
rename from services/java/com/android/server/status/Ticker.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
index e47185b..07e8653 100644
--- a/services/java/com/android/server/status/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
-
-import com.android.internal.R;
+package com.android.systemui.statusbar;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -35,12 +33,24 @@
import java.util.ArrayList;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarNotification;
+import com.android.internal.util.CharSequences;
+import com.android.systemui.R;
-abstract class Ticker {
+public abstract class Ticker {
private static final int TICKER_SEGMENT_DELAY = 3000;
+ private Context mContext;
+ private Handler mHandler = new Handler();
+ private ArrayList<Segment> mSegments = new ArrayList();
+ private TextPaint mPaint;
+ private View mTickerView;
+ private ImageSwitcher mIconSwitcher;
+ private TextSwitcher mTextSwitcher;
+
private final class Segment {
- NotificationData notificationData;
+ StatusBarNotification notification;
Drawable icon;
CharSequence text;
int current;
@@ -116,8 +126,8 @@
return null;
}
- Segment(NotificationData n, Drawable icon, CharSequence text) {
- this.notificationData = n;
+ Segment(StatusBarNotification n, Drawable icon, CharSequence text) {
+ this.notification = n;
this.icon = icon;
this.text = text;
int index = 0;
@@ -131,14 +141,8 @@
}
};
- private Handler mHandler = new Handler();
- private ArrayList<Segment> mSegments = new ArrayList();
- private TextPaint mPaint;
- private View mTickerView;
- private ImageSwitcher mIconSwitcher;
- private TextSwitcher mTextSwitcher;
-
Ticker(Context context, StatusBarView sb) {
+ mContext = context;
mTickerView = sb.findViewById(R.id.ticker);
mIconSwitcher = (ImageSwitcher)sb.findViewById(R.id.tickerIcon);
@@ -158,20 +162,34 @@
mPaint = text.getPaint();
}
- void addEntry(NotificationData n, Drawable icon, CharSequence text) {
+
+ void addEntry(StatusBarNotification n) {
int initialCount = mSegments.size();
- Segment newSegment = new Segment(n, icon, text);
+ // If what's being displayed has the same text and icon, just drop it
+ // (which will let the current one finish, this happens when apps do
+ // a notification storm).
+ if (initialCount > 0) {
+ final Segment seg = mSegments.get(0);
+ if (n.pkg.equals(seg.notification.pkg)
+ && n.notification.icon == seg.notification.notification.icon
+ && n.notification.iconLevel == seg.notification.notification.iconLevel
+ && CharSequences.equals(seg.notification.notification.tickerText,
+ n.notification.tickerText)) {
+ return;
+ }
+ }
- // prune out any preexisting ones for this notification, but not the current one.
- // let that finish, even if it's the same id
- for (int i=1; i<initialCount; i++) {
+ final Drawable icon = StatusBarIconView.getIcon(mContext,
+ new StatusBarIcon(n.pkg, n.notification.icon, n.notification.iconLevel, 0));
+ final Segment newSegment = new Segment(n, icon, n.notification.tickerText);
+
+ // If there's already a notification schedule for this package and id, remove it.
+ for (int i=0; i<initialCount; i++) {
Segment seg = mSegments.get(i);
- if (n.id == seg.notificationData.id && n.pkg.equals(seg.notificationData.pkg)) {
+ if (n.id == seg.notification.id && n.pkg.equals(seg.notification.pkg)) {
// just update that one to use this new data instead
- mSegments.set(i, newSegment);
- // and since we know initialCount != 0, just return
- return ;
+ mSegments.remove(i);
}
}
@@ -194,6 +212,15 @@
}
}
+ void removeEntry(StatusBarNotification n) {
+ for (int i=mSegments.size()-1; i>=0; i--) {
+ Segment seg = mSegments.get(i);
+ if (n.id == seg.notification.id && n.pkg.equals(seg.notification.pkg)) {
+ mSegments.remove(i);
+ }
+ }
+ }
+
void halt() {
mHandler.removeCallbacks(mAdvanceTicker);
mSegments.clear();
diff --git a/services/java/com/android/server/status/TickerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
similarity index 95%
rename from services/java/com/android/server/status/TickerView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
index 099dffb..9749ae4 100644
--- a/services/java/com/android/server/status/TickerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/services/java/com/android/server/status/TrackingPatternView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingPatternView.java
similarity index 97%
rename from services/java/com/android/server/status/TrackingPatternView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/TrackingPatternView.java
index 2c91aa4..ba6f15d 100644
--- a/services/java/com/android/server/status/TrackingPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingPatternView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.content.Context;
import android.content.res.TypedArray;
diff --git a/services/java/com/android/server/status/TrackingView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
similarity index 93%
rename from services/java/com/android/server/status/TrackingView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
index 8ec39c0..c59eb6a 100644
--- a/services/java/com/android/server/status/TrackingView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.systemui.statusbar;
import android.content.Context;
import android.util.AttributeSet;
@@ -26,7 +26,7 @@
public class TrackingView extends LinearLayout {
final Display mDisplay;
- StatusBarService mService;
+ PhoneStatusBarService mService;
boolean mTracking;
int mStartX, mStartY;
@@ -48,7 +48,7 @@
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_BACK:
if (down) {
- mService.deactivate();
+ //mService.deactivate();
}
return true;
}
diff --git a/policy/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/com/android/internal/policy/impl/KeyguardViewMediator.java
index c255041..eb61f5e 100644
--- a/policy/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -145,8 +145,8 @@
private Context mContext;
private AlarmManager mAlarmManager;
private StatusBarManager mStatusBarManager;
- private boolean mShowLockIcon = false;
- private IBinder mSecureLockIcon = null;
+ private boolean mShowLockIcon;
+ private boolean mShowingLockIcon;
private boolean mSystemReady;
@@ -1036,14 +1036,15 @@
if (mShowLockIcon) {
// Give feedback to user when secure keyguard is active and engaged
if (mShowing && isSecure()) {
- if (mSecureLockIcon == null) {
- mSecureLockIcon = mStatusBarManager.addIcon("secure",
- com.android.internal.R.drawable.stat_sys_secure, 0);
+ if (!mShowingLockIcon) {
+ mStatusBarManager.setIcon("secure",
+ com.android.internal.R.drawable.stat_sys_secure, 0);
+ mShowingLockIcon = true;
}
} else {
- if (mSecureLockIcon != null) {
- mStatusBarManager.removeIcon(mSecureLockIcon);
- mSecureLockIcon = null;
+ if (mShowingLockIcon) {
+ mStatusBarManager.removeIcon("secure");
+ mShowingLockIcon = false;
}
}
}
diff --git a/policy/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/com/android/internal/policy/impl/PhoneWindowManager.java
index a7c278b..73a57ee 100755
--- a/policy/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -19,7 +19,6 @@
import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
-import android.app.IStatusBar;
import android.app.IUiModeManager;
import android.app.UiModeManager;
import android.content.ActivityNotFoundException;
@@ -47,6 +46,7 @@
import android.provider.Settings;
import com.android.internal.policy.PolicyManager;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.telephony.ITelephony;
import com.android.internal.widget.PointerLocationView;
@@ -1140,22 +1140,6 @@
return true;
}
}
- } else if (code == KeyEvent.KEYCODE_NOTIFICATION) {
- if (down) {
- // this key doesn't exist on current hardware, but if a device
- // didn't have a touchscreen, it would want one of these to open
- // the status bar.
- IStatusBar sbs = IStatusBar.Stub.asInterface(ServiceManager.getService("statusbar"));
- if (sbs != null) {
- try {
- sbs.toggle();
- } catch (RemoteException e) {
- // we're screwed anyway, since it's in this process
- throw new RuntimeException(e);
- }
- }
- }
- return true;
} else if (code == KeyEvent.KEYCODE_SEARCH) {
if (down) {
if (repeatCount == 0) {
@@ -1537,11 +1521,11 @@
}
if (changes != 0 && hiding) {
- IStatusBar sbs = IStatusBar.Stub.asInterface(ServiceManager.getService("statusbar"));
+ IStatusBarService sbs = IStatusBarService.Stub.asInterface(ServiceManager.getService("statusbar"));
if (sbs != null) {
try {
// Make sure the window shade is hidden.
- sbs.deactivate();
+ sbs.collapse();
} catch (RemoteException e) {
}
}
diff --git a/preloaded-classes b/preloaded-classes
index f300fe7..b602039 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -31,7 +31,6 @@
android.app.IActivityManager
android.app.IActivityManager$ContentProviderHolder
android.app.IAlarmManager$Stub
-android.app.IStatusBar$Stub
android.app.ITransientNotification$Stub
android.app.Instrumentation
android.app.IntentService
@@ -654,6 +653,11 @@
com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState
com.android.internal.policy.impl.PhoneWindowManager
com.android.internal.policy.impl.Policy
+com.android.internal.statusbar.IStatusBar
+com.android.internal.statusbar.IStatusBar$Stub
+com.android.internal.statusbar.IStatusBarService$Stub
+com.android.internal.statusbar.IStatusBarService$Stub
+com.android.internal.statusbar.StatusBarIcon
com.android.internal.telephony.GsmAlphabet
com.android.internal.telephony.ITelephony$Stub
com.android.internal.telephony.ITelephony$Stub$Proxy
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 5bf66e4..a1f26f2 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -26,8 +26,7 @@
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
-import com.android.server.status.IconData;
-import com.android.server.status.StatusBarService;
+import com.android.server.status.StatusBarManagerService;
import org.xmlpull.v1.XmlPullParserException;
@@ -110,9 +109,7 @@
final Context mContext;
final Handler mHandler;
final SettingsObserver mSettingsObserver;
- final StatusBarService mStatusBar;
- final IBinder mInputMethodIcon;
- final IconData mInputMethodData;
+ final StatusBarManagerService mStatusBar;
final IWindowManager mIWindowManager;
final HandlerCaller mCaller;
@@ -447,7 +444,7 @@
}
}
- public InputMethodManagerService(Context context, StatusBarService statusBar) {
+ public InputMethodManagerService(Context context, StatusBarManagerService statusBar) {
mContext = context;
mHandler = new Handler(this);
mIWindowManager = IWindowManager.Stub.asInterface(
@@ -508,9 +505,7 @@
}
mStatusBar = statusBar;
- mInputMethodData = IconData.makeIcon("ime", null, 0, 0, 0);
- mInputMethodIcon = statusBar.addIcon(mInputMethodData, null);
- statusBar.setIconVisibility(mInputMethodIcon, false);
+ statusBar.setIconVisibility("ime", false);
mSettingsObserver = new SettingsObserver(mHandler);
updateFromSettingsLocked();
@@ -912,7 +907,7 @@
mEnabledSession = null;
mCurMethod = null;
}
- mStatusBar.setIconVisibility(mInputMethodIcon, false);
+ mStatusBar.setIconVisibility("ime", false);
}
public void onServiceDisconnected(ComponentName name) {
@@ -946,13 +941,11 @@
synchronized (mMethodMap) {
if (iconId == 0) {
if (DEBUG) Slog.d(TAG, "hide the small icon for the input method");
- mStatusBar.setIconVisibility(mInputMethodIcon, false);
+ mStatusBar.setIconVisibility("ime", false);
} else if (packageName != null) {
if (DEBUG) Slog.d(TAG, "show a small icon for the input method");
- mInputMethodData.iconId = iconId;
- mInputMethodData.iconPackage = packageName;
- mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null);
- mStatusBar.setIconVisibility(mInputMethodIcon, true);
+ mStatusBar.setIcon("ime", packageName, iconId, 0);
+ mStatusBar.setIconVisibility("ime", true);
}
}
} finally {
@@ -1734,8 +1727,6 @@
p.println(" sessionRequested=" + ci.sessionRequested);
p.println(" curSession=" + ci.curSession);
}
- p.println(" mInputMethodIcon=" + mInputMethodIcon);
- p.println(" mInputMethodData=" + mInputMethodData);
p.println(" mCurMethodId=" + mCurMethodId);
client = mCurClient;
p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq);
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 73d17ea..ac3b23b 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -16,9 +16,8 @@
package com.android.server;
-import com.android.server.status.IconData;
-import com.android.server.status.NotificationData;
-import com.android.server.status.StatusBarService;
+import com.android.internal.statusbar.StatusBarNotification;
+import com.android.server.status.StatusBarManagerService;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
@@ -86,7 +85,7 @@
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
- private StatusBarService mStatusBarService;
+ private StatusBarManagerService mStatusBar;
private LightsService mLightsService;
private LightsService.Light mBatteryLight;
private LightsService.Light mNotificationLight;
@@ -238,8 +237,8 @@
}
}
- private StatusBarService.NotificationCallbacks mNotificationCallbacks
- = new StatusBarService.NotificationCallbacks() {
+ private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
+ = new StatusBarManagerService.NotificationCallbacks() {
public void onSetDisabled(int status) {
synchronized (mNotificationList) {
@@ -405,7 +404,7 @@
}
}
- NotificationManagerService(Context context, StatusBarService statusBar,
+ NotificationManagerService(Context context, StatusBarManagerService statusBar,
LightsService lights)
{
super();
@@ -417,7 +416,7 @@
mToastQueue = new ArrayList<ToastRecord>();
mHandler = new WorkerHandler();
- mStatusBarService = statusBar;
+ mStatusBar = statusBar;
statusBar.setNotificationCallbacks(mNotificationCallbacks);
mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
@@ -705,36 +704,12 @@
}
if (notification.icon != 0) {
- IconData icon = IconData.makeIcon(null, pkg, notification.icon,
- notification.iconLevel,
- notification.number);
- CharSequence truncatedTicker = notification.tickerText;
-
- // TODO: make this restriction do something smarter like never fill
- // more than two screens. "Why would anyone need more than 80 characters." :-/
- final int maxTickerLen = 80;
- if (truncatedTicker != null && truncatedTicker.length() > maxTickerLen) {
- truncatedTicker = truncatedTicker.subSequence(0, maxTickerLen);
- }
-
- NotificationData n = new NotificationData();
- n.pkg = pkg;
- n.tag = tag;
- n.id = id;
- n.when = notification.when;
- n.tickerText = truncatedTicker;
- n.ongoingEvent = (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
- if (!n.ongoingEvent && (notification.flags & Notification.FLAG_NO_CLEAR) == 0) {
- n.clearable = true;
- }
- n.contentView = notification.contentView;
- n.contentIntent = notification.contentIntent;
- n.deleteIntent = notification.deleteIntent;
+ StatusBarNotification n = new StatusBarNotification(pkg, id, tag, notification);
if (old != null && old.statusBarKey != null) {
r.statusBarKey = old.statusBarKey;
long identity = Binder.clearCallingIdentity();
try {
- mStatusBarService.updateIcon(r.statusBarKey, icon, n);
+ mStatusBar.updateNotification(r.statusBarKey, n);
}
finally {
Binder.restoreCallingIdentity(identity);
@@ -742,21 +717,19 @@
} else {
long identity = Binder.clearCallingIdentity();
try {
- r.statusBarKey = mStatusBarService.addIcon(icon, n);
+ r.statusBarKey = mStatusBar.addNotification(n);
mAttentionLight.pulse();
}
finally {
Binder.restoreCallingIdentity(identity);
}
}
-
sendAccessibilityEvent(notification, pkg);
-
} else {
if (old != null && old.statusBarKey != null) {
long identity = Binder.clearCallingIdentity();
try {
- mStatusBarService.removeIcon(old.statusBarKey);
+ mStatusBar.removeNotification(old.statusBarKey);
}
finally {
Binder.restoreCallingIdentity(identity);
@@ -864,7 +837,7 @@
if (r.notification.icon != 0) {
long identity = Binder.clearCallingIdentity();
try {
- mStatusBarService.removeIcon(r.statusBarKey);
+ mStatusBar.removeNotification(r.statusBarKey);
}
finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9d5d035..4307cdc 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -17,7 +17,7 @@
package com.android.server;
import com.android.server.am.ActivityManagerService;
-import com.android.server.status.StatusBarService;
+import com.android.server.status.StatusBarManagerService;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
@@ -206,7 +206,7 @@
}
DevicePolicyManagerService devicePolicy = null;
- StatusBarService statusBar = null;
+ StatusBarManagerService statusBar = null;
InputMethodManagerService imm = null;
AppWidgetService appWidget = null;
NotificationManagerService notification = null;
@@ -224,10 +224,10 @@
try {
Slog.i(TAG, "Status Bar");
- statusBar = new StatusBarService(context);
+ statusBar = new StatusBarManagerService(context);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
} catch (Throwable e) {
- Slog.e(TAG, "Failure starting StatusBarService", e);
+ Slog.e(TAG, "Failure starting StatusBarManagerService", e);
}
try {
@@ -405,12 +405,6 @@
}
try {
- com.android.server.status.StatusBarPolicy.installIcons(context, statusBar);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure installing status bar icons", e);
- }
-
- try {
Slog.i(TAG, "DiskStats Service");
ServiceManager.addService("diskstats", new DiskStatsService(context));
} catch (Throwable e) {
@@ -464,6 +458,7 @@
}
// These are needed to propagate to the runnable below.
+ final StatusBarManagerService statusBarF = statusBar;
final BatteryService batteryF = battery;
final ConnectivityService connectivityF = connectivity;
final DockObserver dockF = dock;
@@ -485,6 +480,7 @@
public void run() {
Slog.i(TAG, "Making services ready");
+ if (statusBarF != null) statusBarF.systemReady2();
if (batteryF != null) batteryF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
if (dockF != null) dockF.systemReady();
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 3606629..9493161 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -545,7 +545,7 @@
mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
}
- // Fear not: StatusBarService manages a list of requests to disable
+ // Fear not: StatusBarManagerService manages a list of requests to disable
// features of the status bar; these are ORed together to form the
// active disabled list. So if (for example) the device is locked and
// the status bar should be totally disabled, the calls below will
diff --git a/services/java/com/android/server/status/IconData.java b/services/java/com/android/server/status/IconData.java
deleted file mode 100644
index fd226f9..0000000
--- a/services/java/com/android/server/status/IconData.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.status;
-
-import android.util.Slog;
-
-public class IconData {
- /**
- * Indicates ths item represents a piece of text.
- */
- public static final int TEXT = 1;
-
- /**
- * Indicates ths item represents an icon.
- */
- public static final int ICON = 2;
-
- /**
- * The type of this item. One of TEXT, ICON, or LEVEL_ICON.
- */
- public int type;
-
- /**
- * The slot that this icon will be in if it is not a notification
- */
- public String slot;
-
- /**
- * The package containting the icon to draw for this item. Valid if this is
- * an ICON type.
- */
- public String iconPackage;
-
- /**
- * The icon to draw for this item. Valid if this is an ICON type.
- */
- public int iconId;
-
- /**
- * The level associated with the icon. Valid if this is a LEVEL_ICON type.
- */
- public int iconLevel;
-
- /**
- * The "count" number.
- */
- public int number;
-
- /**
- * The text associated with the icon. Valid if this is a TEXT type.
- */
- public CharSequence text;
-
- private IconData() {
- }
-
- public static IconData makeIcon(String slot,
- String iconPackage, int iconId, int iconLevel, int number) {
- IconData data = new IconData();
- data.type = ICON;
- data.slot = slot;
- data.iconPackage = iconPackage;
- data.iconId = iconId;
- data.iconLevel = iconLevel;
- data.number = number;
- return data;
- }
-
- public static IconData makeText(String slot, CharSequence text) {
- IconData data = new IconData();
- data.type = TEXT;
- data.slot = slot;
- data.text = text;
- return data;
- }
-
- public void copyFrom(IconData that) {
- this.type = that.type;
- this.slot = that.slot;
- this.iconPackage = that.iconPackage;
- this.iconId = that.iconId;
- this.iconLevel = that.iconLevel;
- this.number = that.number;
- this.text = that.text; // should we clone this?
- }
-
- public IconData clone() {
- IconData that = new IconData();
- that.copyFrom(this);
- return that;
- }
-
- public String toString() {
- if (this.type == TEXT) {
- return "IconData(slot=" + (this.slot != null ? "'" + this.slot + "'" : "null")
- + " text='" + this.text + "')";
- }
- else if (this.type == ICON) {
- return "IconData(slot=" + (this.slot != null ? "'" + this.slot + "'" : "null")
- + " package=" + this.iconPackage
- + " iconId=" + Integer.toHexString(this.iconId)
- + " iconLevel=" + this.iconLevel + ")";
- }
- else {
- return "IconData(type=" + type + ")";
- }
- }
-}
diff --git a/services/java/com/android/server/status/NotificationData.java b/services/java/com/android/server/status/NotificationData.java
deleted file mode 100644
index 71f01ca..0000000
--- a/services/java/com/android/server/status/NotificationData.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.status;
-
-import android.app.PendingIntent;
-import android.widget.RemoteViews;
-
-public class NotificationData {
- public String pkg;
- public String tag;
- public int id;
- public CharSequence tickerText;
-
- public long when;
- public boolean ongoingEvent;
- public boolean clearable;
-
- public RemoteViews contentView;
- public PendingIntent contentIntent;
-
- public PendingIntent deleteIntent;
-
- public String toString() {
- return "NotificationData(package=" + pkg + " id=" + id + " tickerText=" + tickerText
- + " ongoingEvent=" + ongoingEvent + " contentIntent=" + contentIntent
- + " deleteIntent=" + deleteIntent
- + " clearable=" + clearable
- + " contentView=" + contentView + " when=" + when + ")";
- }
-}
diff --git a/services/java/com/android/server/status/NotificationViewList.java b/services/java/com/android/server/status/NotificationViewList.java
deleted file mode 100644
index 1bb56a7..0000000
--- a/services/java/com/android/server/status/NotificationViewList.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.status;
-
-import android.os.IBinder;
-import android.util.Slog;
-import android.view.View;
-import java.util.ArrayList;
-
-class NotificationViewList {
- private ArrayList<StatusBarNotification> mOngoing = new ArrayList();
- private ArrayList<StatusBarNotification> mLatest = new ArrayList();
-
- NotificationViewList() {
- }
-
- private static final int indexInList(ArrayList<StatusBarNotification> list, NotificationData n){
- final int N = list.size();
- for (int i=0; i<N; i++) {
- StatusBarNotification that = list.get(i);
- if (that.data == n) {
- return i;
- }
- }
- return -1;
- }
-
- int getIconIndex(NotificationData n) {
- final int ongoingSize = mOngoing.size();
- final int latestSize = mLatest.size();
- if (n.ongoingEvent) {
- int index = indexInList(mOngoing, n);
- if (index >= 0) {
- return latestSize + index + 1;
- } else {
- return -1;
- }
- } else {
- return indexInList(mLatest, n) + 1;
- }
- }
-
- void remove(StatusBarNotification notification) {
- NotificationData n = notification.data;
- int index;
- index = indexInList(mOngoing, n);
- if (index >= 0) {
- mOngoing.remove(index);
- return;
- }
- index = indexInList(mLatest, n);
- if (index >= 0) {
- mLatest.remove(index);
- return;
- }
- }
-
- ArrayList<StatusBarNotification> notificationsForPackage(String packageName) {
- ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>();
- int N = mOngoing.size();
- for (int i=0; i<N; i++) {
- if (matchPackage(mOngoing.get(i), packageName)) {
- list.add(mOngoing.get(i));
- }
- }
- N = mLatest.size();
- for (int i=0; i<N; i++) {
- if (matchPackage(mLatest.get(i), packageName)) {
- list.add(mLatest.get(i));
- }
- }
- return list;
- }
-
- private final boolean matchPackage(StatusBarNotification snb, String packageName) {
- if (snb.data.contentIntent != null) {
- if (snb.data.contentIntent.getTargetPackage().equals(packageName)) {
- return true;
- }
- } else if (snb.data.pkg != null && snb.data.pkg.equals(packageName)) {
- return true;
- }
- return false;
- }
-
- private static final int indexForKey(ArrayList<StatusBarNotification> list, IBinder key) {
- final int N = list.size();
- for (int i=0; i<N; i++) {
- if (list.get(i).key == key) {
- return i;
- }
- }
- return -1;
- }
-
- StatusBarNotification get(IBinder key) {
- int index;
- index = indexForKey(mOngoing, key);
- if (index >= 0) {
- return mOngoing.get(index);
- }
- index = indexForKey(mLatest, key);
- if (index >= 0) {
- return mLatest.get(index);
- }
- return null;
- }
-
- // gets the index of the notification's view in its expanded parent view
- int getExpandedIndex(StatusBarNotification notification) {
- ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
- final IBinder key = notification.key;
- int index = 0;
- // (the view order is backwards from this list order)
- for (int i=list.size()-1; i>=0; i--) {
- StatusBarNotification item = list.get(i);
- if (item.key == key) {
- return index;
- }
- if (item.view != null) {
- index++;
- }
- }
- Slog.e(StatusBarService.TAG, "Couldn't find notification in NotificationViewList.");
- Slog.e(StatusBarService.TAG, "notification=" + notification);
- dump(notification);
- return 0;
- }
-
- void clearViews() {
- int N = mOngoing.size();
- for (int i=0; i<N; i++) {
- mOngoing.get(i).view = null;
- }
- N = mLatest.size();
- for (int i=0; i<N; i++) {
- mLatest.get(i).view = null;
- }
- }
-
- int ongoingCount() {
- return mOngoing.size();
- }
-
- int latestCount() {
- return mLatest.size();
- }
-
- StatusBarNotification getOngoing(int index) {
- return mOngoing.get(index);
- }
-
- StatusBarNotification getLatest(int index) {
- return mLatest.get(index);
- }
-
- int size() {
- return mOngoing.size() + mLatest.size();
- }
-
- void add(StatusBarNotification notification) {
- if (StatusBarService.SPEW) {
- Slog.d(StatusBarService.TAG, "before add NotificationViewList"
- + " notification.data.ongoingEvent=" + notification.data.ongoingEvent);
- dump(notification);
- }
-
- ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
- long when = notification.data.when;
- final int N = list.size();
- int index = N;
- for (int i=0; i<N; i++) {
- StatusBarNotification that = list.get(i);
- if (that.data.when > when) {
- index = i;
- break;
- }
- }
- list.add(index, notification);
-
- if (StatusBarService.SPEW) {
- Slog.d(StatusBarService.TAG, "after add NotificationViewList index=" + index);
- dump(notification);
- }
- }
-
- void dump(StatusBarNotification notification) {
- if (StatusBarService.SPEW) {
- boolean showTime = false;
- String s = "";
- for (int i=0; i<mOngoing.size(); i++) {
- StatusBarNotification that = mOngoing.get(i);
- if (that.key == notification.key) {
- s += "[";
- }
- if (showTime) {
- s += that.data.when;
- } else {
- s += that.data.pkg + "/" + that.data.id + "/" + that.view;
- }
- if (that.key == notification.key) {
- s += "]";
- }
- s += " ";
- }
- Slog.d(StatusBarService.TAG, "NotificationViewList ongoing: " + s);
-
- s = "";
- for (int i=0; i<mLatest.size(); i++) {
- StatusBarNotification that = mLatest.get(i);
- if (that.key == notification.key) {
- s += "[";
- }
- if (showTime) {
- s += that.data.when;
- } else {
- s += that.data.pkg + "/" + that.data.id + "/" + that.view;
- }
- if (that.key == notification.key) {
- s += "]";
- }
- s += " ";
- }
- Slog.d(StatusBarService.TAG, "NotificationViewList latest: " + s);
- }
- }
-
- StatusBarNotification get(View view) {
- int N = mOngoing.size();
- for (int i=0; i<N; i++) {
- StatusBarNotification notification = mOngoing.get(i);
- View v = notification.view;
- if (v == view) {
- return notification;
- }
- }
- N = mLatest.size();
- for (int i=0; i<N; i++) {
- StatusBarNotification notification = mLatest.get(i);
- View v = notification.view;
- if (v == view) {
- return notification;
- }
- }
- return null;
- }
-
- void update(StatusBarNotification notification) {
- remove(notification);
- add(notification);
- }
-
- boolean hasClearableItems() {
- int N = mLatest.size();
- for (int i=0; i<N; i++) {
- if (mLatest.get(i).data.clearable) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/services/java/com/android/server/status/StatusBarException.java b/services/java/com/android/server/status/StatusBarException.java
deleted file mode 100644
index be58f59..0000000
--- a/services/java/com/android/server/status/StatusBarException.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.status;
-
-public class StatusBarException extends RuntimeException {
- StatusBarException(String msg) {
- super(msg);
- }
-}
diff --git a/services/java/com/android/server/status/StatusBarIcon.java b/services/java/com/android/server/status/StatusBarIcon.java
deleted file mode 100644
index f77b550..0000000
--- a/services/java/com/android/server/status/StatusBarIcon.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.status;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-class StatusBarIcon {
- // TODO: get this from a resource
- private static final int ICON_GAP = 8;
- private static final int ICON_WIDTH = 25;
- private static final int ICON_HEIGHT = 25;
-
- public View view;
-
- IconData mData;
-
- private TextView mTextView;
- private AnimatedImageView mImageView;
- private TextView mNumberView;
-
- public StatusBarIcon(Context context, IconData data, ViewGroup parent) {
- mData = data.clone();
-
- switch (data.type) {
- case IconData.TEXT: {
- TextView t;
- t = new TextView(context, null, com.android.internal.R.style.TextAppearance_StatusBar_Icon);
- mTextView = t;
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.MATCH_PARENT);
- t.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
- t.setPadding(6, 0, 0, 0);
- t.setLayoutParams(layoutParams);
- t.setText(data.text);
- this.view = t;
- break;
- }
-
- case IconData.ICON: {
- // container
- LayoutInflater inflater = (LayoutInflater)context.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View v = inflater.inflate(com.android.internal.R.layout.status_bar_icon, parent, false);
- this.view = v;
-
- // icon
- AnimatedImageView im = (AnimatedImageView)v.findViewById(com.android.internal.R.id.image);
- im.setImageDrawable(getIcon(context, data));
- im.setImageLevel(data.iconLevel);
- mImageView = im;
-
- // number
- TextView nv = (TextView)v.findViewById(com.android.internal.R.id.number);
- mNumberView = nv;
- if (data.number > 0) {
- nv.setText("" + data.number);
- nv.setVisibility(View.VISIBLE);
- } else {
- nv.setVisibility(View.GONE);
- }
- break;
- }
- }
- }
-
- public void update(Context context, IconData data) throws StatusBarException {
- if (mData.type != data.type) {
- throw new StatusBarException("status bar entry type can't change");
- }
- switch (data.type) {
- case IconData.TEXT:
- if (!TextUtils.equals(mData.text, data.text)) {
- TextView tv = mTextView;
- tv.setText(data.text);
- }
- break;
- case IconData.ICON:
- if (((mData.iconPackage != null && data.iconPackage != null)
- && !mData.iconPackage.equals(data.iconPackage))
- || mData.iconId != data.iconId
- || mData.iconLevel != data.iconLevel) {
- ImageView im = mImageView;
- im.setImageDrawable(getIcon(context, data));
- im.setImageLevel(data.iconLevel);
- }
- if (mData.number != data.number) {
- TextView nv = mNumberView;
- if (data.number > 0) {
- nv.setText("" + data.number);
- } else {
- nv.setText("");
- }
- }
- break;
- }
- mData.copyFrom(data);
- }
-
- public void update(int number) {
- if (mData.number != number) {
- TextView nv = mNumberView;
- if (number > 0) {
- nv.setText("" + number);
- } else {
- nv.setText("");
- }
- }
- mData.number = number;
- }
-
-
- /**
- * Returns the right icon to use for this item, respecting the iconId and
- * iconPackage (if set)
- *
- * @param context Context to use to get resources if iconPackage is not set
- * @return Drawable for this item, or null if the package or item could not
- * be found
- */
- static Drawable getIcon(Context context, IconData data) {
-
- Resources r = null;
-
- if (data.iconPackage != null) {
- try {
- r = context.getPackageManager().getResourcesForApplication(data.iconPackage);
- } catch (PackageManager.NameNotFoundException ex) {
- Slog.e(StatusBarService.TAG, "Icon package not found: " + data.iconPackage, ex);
- return null;
- }
- } else {
- r = context.getResources();
- }
-
- if (data.iconId == 0) {
- Slog.w(StatusBarService.TAG, "No icon ID for slot " + data.slot);
- return null;
- }
-
- try {
- return r.getDrawable(data.iconId);
- } catch (RuntimeException e) {
- Slog.w(StatusBarService.TAG, "Icon not found in "
- + (data.iconPackage != null ? data.iconId : "<system>")
- + ": " + Integer.toHexString(data.iconId));
- }
-
- return null;
- }
-
- int getNumber() {
- return mData.number;
- }
-}
-
diff --git a/services/java/com/android/server/status/StatusBarManagerService.java b/services/java/com/android/server/status/StatusBarManagerService.java
new file mode 100644
index 0000000..0af1ebb
--- /dev/null
+++ b/services/java/com/android/server/status/StatusBarManagerService.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2007 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.status;
+
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIconList;
+import com.android.internal.statusbar.StatusBarNotification;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * A note on locking: We rely on the fact that calls onto mBar are oneway or
+ * if they are local, that they just enqueue messages to not deadlock.
+ */
+public class StatusBarManagerService extends IStatusBarService.Stub
+{
+ static final String TAG = "StatusBarManagerService";
+ static final boolean SPEW = true;
+
+ public static final String ACTION_STATUSBAR_START
+ = "com.android.internal.policy.statusbar.START";
+
+ final Context mContext;
+ Handler mHandler = new Handler();
+ NotificationCallbacks mNotificationCallbacks;
+ volatile IStatusBar mBar;
+ StatusBarIconList mIcons = new StatusBarIconList();
+ HashMap<IBinder,StatusBarNotification> mNotifications
+ = new HashMap<IBinder,StatusBarNotification>();
+
+ // for disabling the status bar
+ ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
+ int mDisabled = 0;
+
+ private class DisableRecord implements IBinder.DeathRecipient {
+ String pkg;
+ int what;
+ IBinder token;
+
+ public void binderDied() {
+ Slog.i(TAG, "binder died for pkg=" + pkg);
+ disable(0, token, pkg);
+ token.unlinkToDeath(this, 0);
+ }
+ }
+
+ public interface NotificationCallbacks {
+ void onSetDisabled(int status);
+ void onClearAll();
+ void onNotificationClick(String pkg, String tag, int id);
+ void onPanelRevealed();
+ }
+
+ /**
+ * Construct the service, add the status bar view to the window manager
+ */
+ public StatusBarManagerService(Context context) {
+ mContext = context;
+
+ final Resources res = context.getResources();
+ mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.status_bar_icon_order));
+ }
+
+ public void setNotificationCallbacks(NotificationCallbacks listener) {
+ mNotificationCallbacks = listener;
+ }
+
+ // ================================================================================
+ // Constructing the view
+ // ================================================================================
+
+ public void systemReady() {
+ }
+
+ public void systemReady2() {
+ // Start the status bar app
+ Intent intent = new Intent(ACTION_STATUSBAR_START);
+ mContext.sendBroadcast(intent /** permission **/);
+ }
+
+ // ================================================================================
+ // From IStatusBarService
+ // ================================================================================
+ public void expand() {
+ enforceExpandStatusBar();
+
+ if (mBar != null) {
+ try {
+ mBar.animateExpand();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ public void collapse() {
+ enforceExpandStatusBar();
+
+ if (mBar != null) {
+ try {
+ mBar.animateCollapse();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ public void disable(int what, IBinder token, String pkg) {
+ enforceStatusBar();
+
+ // It's important that the the callback and the call to mBar get done
+ // in the same order when multiple threads are calling this function
+ // so they are paired correctly. The messages on the handler will be
+ // handled in the order they were enqueued, but will be outside the lock.
+ synchronized (mDisableRecords) {
+ manageDisableListLocked(what, token, pkg);
+ final int net = gatherDisableActionsLocked();
+ Slog.d(TAG, "disable... net=0x" + Integer.toHexString(net));
+ if (net != mDisabled) {
+ mDisabled = net;
+ mHandler.post(new Runnable() {
+ public void run() {
+ mNotificationCallbacks.onSetDisabled(net);
+ }
+ });
+ if (mBar != null) {
+ try {
+ mBar.disable(net);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+ }
+
+ public void setIcon(String slot, String iconPackage, int iconId, int iconLevel) {
+ enforceStatusBar();
+
+ synchronized (mIcons) {
+ int index = mIcons.getSlotIndex(slot);
+ if (index < 0) {
+ throw new SecurityException("invalid status bar icon slot: " + slot);
+ }
+
+ StatusBarIcon icon = new StatusBarIcon(iconPackage, iconId, iconLevel);
+ //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
+ mIcons.setIcon(index, icon);
+
+ if (mBar != null) {
+ try {
+ mBar.setIcon(index, icon);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ public void setIconVisibility(String slot, boolean visible) {
+ enforceStatusBar();
+
+ synchronized (mIcons) {
+ int index = mIcons.getSlotIndex(slot);
+ if (index < 0) {
+ throw new SecurityException("invalid status bar icon slot: " + slot);
+ }
+
+ StatusBarIcon icon = mIcons.getIcon(index);
+ if (icon == null) {
+ return;
+ }
+
+ if (icon.visible != visible) {
+ icon.visible = visible;
+
+ if (mBar != null) {
+ try {
+ mBar.setIcon(index, icon);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+ }
+
+ public void removeIcon(String slot) {
+ enforceStatusBar();
+
+ synchronized (mIcons) {
+ int index = mIcons.getSlotIndex(slot);
+ if (index < 0) {
+ throw new SecurityException("invalid status bar icon slot: " + slot);
+ }
+
+ mIcons.removeIcon(index);
+
+ if (mBar != null) {
+ try {
+ mBar.removeIcon(index);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ private void enforceStatusBar() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
+ "StatusBarManagerService");
+ }
+
+ private void enforceExpandStatusBar() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR,
+ "StatusBarManagerService");
+ }
+
+
+ // ================================================================================
+ // Callbacks from the status bar service.
+ // ================================================================================
+ public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
+ List<IBinder> notificationKeys, List<StatusBarNotification> notifications) {
+ Slog.i(TAG, "registerStatusBar bar=" + bar);
+ mBar = bar;
+ synchronized (mIcons) {
+ iconList.copyFrom(mIcons);
+ }
+ synchronized (mNotifications) {
+ for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
+ notificationKeys.add(e.getKey());
+ notifications.add(e.getValue());
+ }
+ }
+ }
+
+ /**
+ * The status bar service should call this when the user changes whether
+ * the status bar is visible or not.
+ */
+ public void visibilityChanged(boolean visible) {
+ //Slog.d(TAG, "visibilityChanged visible=" + visible);
+ }
+
+ public void onNotificationClick(String pkg, String tag, int id) {
+ mNotificationCallbacks.onNotificationClick(pkg, tag, id);
+ }
+
+ public void onClearAllNotifications() {
+ mNotificationCallbacks.onClearAll();
+ }
+
+ // ================================================================================
+ // Callbacks for NotificationManagerService.
+ // ================================================================================
+ public IBinder addNotification(StatusBarNotification notification) {
+ synchronized (mNotifications) {
+ IBinder key = new Binder();
+ mNotifications.put(key, notification);
+ if (mBar != null) {
+ try {
+ mBar.addNotification(key, notification);
+ } catch (RemoteException ex) {
+ }
+ }
+ return key;
+ }
+ }
+
+ public void updateNotification(IBinder key, StatusBarNotification notification) {
+ synchronized (mNotifications) {
+ if (!mNotifications.containsKey(key)) {
+ throw new IllegalArgumentException("updateNotification key not found: " + key);
+ }
+ mNotifications.put(key, notification);
+ if (mBar != null) {
+ try {
+ mBar.updateNotification(key, notification);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ public void removeNotification(IBinder key) {
+ synchronized (mNotifications) {
+ final StatusBarNotification n = mNotifications.remove(key);
+ if (n == null) {
+ throw new IllegalArgumentException("removeNotification key not found: " + key);
+ }
+ if (mBar != null) {
+ try {
+ mBar.removeNotification(key);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ // ================================================================================
+ // Can be called from any thread
+ // ================================================================================
+
+ // lock on mDisableRecords
+ void manageDisableListLocked(int what, IBinder token, String pkg) {
+ if (SPEW) {
+ Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
+ }
+ // update the list
+ synchronized (mDisableRecords) {
+ final int N = mDisableRecords.size();
+ DisableRecord tok = null;
+ int i;
+ for (i=0; i<N; i++) {
+ DisableRecord t = mDisableRecords.get(i);
+ if (t.token == token) {
+ tok = t;
+ break;
+ }
+ }
+ if (what == 0 || !token.isBinderAlive()) {
+ if (tok != null) {
+ mDisableRecords.remove(i);
+ tok.token.unlinkToDeath(tok, 0);
+ }
+ } else {
+ if (tok == null) {
+ tok = new DisableRecord();
+ try {
+ token.linkToDeath(tok, 0);
+ }
+ catch (RemoteException ex) {
+ return; // give up
+ }
+ mDisableRecords.add(tok);
+ }
+ tok.what = what;
+ tok.token = token;
+ tok.pkg = pkg;
+ }
+ }
+ }
+
+ // lock on mDisableRecords
+ int gatherDisableActionsLocked() {
+ final int N = mDisableRecords.size();
+ // gather the new net flags
+ int net = 0;
+ for (int i=0; i<N; i++) {
+ net |= mDisableRecords.get(i).what;
+ }
+ return net;
+ }
+
+ // ================================================================================
+ // Always called from UI thread
+ // ================================================================================
+
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump StatusBar from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mIcons) {
+ mIcons.dump(pw);
+ }
+
+ synchronized (mNotifications) {
+ int i=0;
+ pw.println("Notification list:");
+ for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
+ pw.printf(" %2d: %s\n", i, e.getValue().toString());
+ i++;
+ }
+ }
+
+ synchronized (mDisableRecords) {
+ final int N = mDisableRecords.size();
+ pw.println(" mDisableRecords.size=" + N
+ + " mDisabled=0x" + Integer.toHexString(mDisabled));
+ for (int i=0; i<N; i++) {
+ DisableRecord tok = mDisableRecords.get(i);
+ pw.println(" [" + i + "] what=0x" + Integer.toHexString(tok.what)
+ + " pkg=" + tok.pkg + " token=" + tok.token);
+ }
+ }
+ }
+
+ /**
+ * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
+ * This was added last-minute and is inconsistent with the way the rest of the notifications
+ * are handled, because the notification isn't really cancelled. The lights are just
+ * turned off. If any other notifications happen, the lights will turn back on. Steve says
+ * this is what he wants. (see bug 1131461)
+ */
+ private boolean mPanelSlightlyVisible;
+ void panelSlightlyVisible(boolean visible) {
+ if (mPanelSlightlyVisible != visible) {
+ mPanelSlightlyVisible = visible;
+ if (visible) {
+ // tell the notification manager to turn off the lights.
+ mNotificationCallbacks.onPanelRevealed();
+ }
+ }
+ }
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+ || Intent.ACTION_SCREEN_OFF.equals(action)) {
+ collapse();
+ }
+ /*
+ else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
+ updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
+ intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
+ }
+ else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+ updateResources();
+ }
+ */
+ }
+ };
+
+}
diff --git a/services/java/com/android/server/status/StatusBarNotification.java b/services/java/com/android/server/status/StatusBarNotification.java
deleted file mode 100644
index e5773f7..0000000
--- a/services/java/com/android/server/status/StatusBarNotification.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.status;
-
-import android.os.IBinder;
-import android.view.View;
-
-class StatusBarNotification {
- IBinder key;
- NotificationData data;
- View view;
- View contentView;
-}
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
deleted file mode 100644
index cab2662..0000000
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ /dev/null
@@ -1,1411 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.status;
-
-import android.app.AlertDialog;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothPbap;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.TypedArray;
-import android.graphics.PixelFormat;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.location.LocationManager;
-import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.net.NetworkInfo;
-import android.net.Uri;
-import android.net.wifi.WifiManager;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.storage.StorageManager;
-import android.provider.Settings;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.TelephonyManager;
-import android.text.format.DateFormat;
-import android.text.style.CharacterStyle;
-import android.text.style.RelativeSizeSpan;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.StyleSpan;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.util.Slog;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.cdma.EriInfo;
-import com.android.internal.telephony.cdma.TtyIntent;
-import com.android.server.am.BatteryStatsService;
-
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.TimeZone;
-
-/**
- * This class contains all of the policy about which icons are installed in the status
- * bar at boot time. In reality, it should go into the android.policy package, but
- * putting it here is the first step from extracting it.
- */
-public class StatusBarPolicy {
- private static final String TAG = "StatusBarPolicy";
-
- private static StatusBarPolicy sInstance;
-
- // message codes for the handler
- private static final int EVENT_BATTERY_CLOSE = 4;
-
- private static final int AM_PM_STYLE_NORMAL = 0;
- private static final int AM_PM_STYLE_SMALL = 1;
- private static final int AM_PM_STYLE_GONE = 2;
-
- private static final int AM_PM_STYLE = AM_PM_STYLE_GONE;
-
- private final Context mContext;
- private final StatusBarService mService;
- private final Handler mHandler = new StatusBarHandler();
- private final IBatteryStats mBatteryStats;
-
- // clock
- private Calendar mCalendar;
- private String mClockFormatString;
- private SimpleDateFormat mClockFormat;
- private IBinder mClockIcon;
- private IconData mClockData;
-
- // storage
- private StorageManager mStorageManager;
-
- // battery
- private IBinder mBatteryIcon;
- private IconData mBatteryData;
- private boolean mBatteryFirst = true;
- private boolean mBatteryPlugged;
- private int mBatteryLevel;
- private AlertDialog mLowBatteryDialog;
- private TextView mBatteryLevelTextView;
- private View mBatteryView;
- private int mBatteryViewSequence;
- private boolean mBatteryShowLowOnEndCall = false;
- private static final boolean SHOW_LOW_BATTERY_WARNING = true;
- private static final boolean SHOW_BATTERY_WARNINGS_IN_CALL = true;
-
- // phone
- private TelephonyManager mPhone;
- private IBinder mPhoneIcon;
-
- //***** Signal strength icons
- private IconData mPhoneData;
- //GSM/UMTS
- private static final int[] sSignalImages = new int[] {
- com.android.internal.R.drawable.stat_sys_signal_0,
- com.android.internal.R.drawable.stat_sys_signal_1,
- com.android.internal.R.drawable.stat_sys_signal_2,
- com.android.internal.R.drawable.stat_sys_signal_3,
- com.android.internal.R.drawable.stat_sys_signal_4
- };
- private static final int[] sSignalImages_r = new int[] {
- com.android.internal.R.drawable.stat_sys_r_signal_0,
- com.android.internal.R.drawable.stat_sys_r_signal_1,
- com.android.internal.R.drawable.stat_sys_r_signal_2,
- com.android.internal.R.drawable.stat_sys_r_signal_3,
- com.android.internal.R.drawable.stat_sys_r_signal_4
- };
- private static final int[] sRoamingIndicatorImages_cdma = new int[] {
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //Standard Roaming Indicator
- // 1 is Standard Roaming Indicator OFF
- // TODO T: image never used, remove and put 0 instead?
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 2 is Standard Roaming Indicator FLASHING
- // TODO T: image never used, remove and put 0 instead?
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 3-12 Standard ERI
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //3
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 13-63 Reserved for Standard ERI
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //13
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 64-127 Reserved for Non Standard (Operator Specific) ERI
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //64
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0 //83
-
- // 128-255 Reserved
- };
-
- //***** Data connection icons
- private int[] mDataIconList = sDataNetType_g;
- //GSM/UMTS
- private static final int[] sDataNetType_g = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_g,
- com.android.internal.R.drawable.stat_sys_data_in_g,
- com.android.internal.R.drawable.stat_sys_data_out_g,
- com.android.internal.R.drawable.stat_sys_data_inandout_g,
- };
- private static final int[] sDataNetType_3g = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_3g,
- com.android.internal.R.drawable.stat_sys_data_in_3g,
- com.android.internal.R.drawable.stat_sys_data_out_3g,
- com.android.internal.R.drawable.stat_sys_data_inandout_3g,
- };
- private static final int[] sDataNetType_e = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_e,
- com.android.internal.R.drawable.stat_sys_data_in_e,
- com.android.internal.R.drawable.stat_sys_data_out_e,
- com.android.internal.R.drawable.stat_sys_data_inandout_e,
- };
- //3.5G
- private static final int[] sDataNetType_h = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_h,
- com.android.internal.R.drawable.stat_sys_data_in_h,
- com.android.internal.R.drawable.stat_sys_data_out_h,
- com.android.internal.R.drawable.stat_sys_data_inandout_h,
- };
-
- //CDMA
- // Use 3G icons for EVDO data and 1x icons for 1XRTT data
- private static final int[] sDataNetType_1x = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_1x,
- com.android.internal.R.drawable.stat_sys_data_in_1x,
- com.android.internal.R.drawable.stat_sys_data_out_1x,
- com.android.internal.R.drawable.stat_sys_data_inandout_1x,
- };
-
- // Assume it's all good unless we hear otherwise. We don't always seem
- // to get broadcasts that it *is* there.
- IccCard.State mSimState = IccCard.State.READY;
- int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
- int mDataState = TelephonyManager.DATA_DISCONNECTED;
- int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
- ServiceState mServiceState;
- SignalStrength mSignalStrength;
-
- // data connection
- private IBinder mDataIcon;
- private IconData mDataData;
- private boolean mDataIconVisible;
- private boolean mHspaDataDistinguishable;
-
- // ringer volume
- private IBinder mVolumeIcon;
- private IconData mVolumeData;
- private boolean mVolumeVisible;
-
- // bluetooth device status
- private IBinder mBluetoothIcon;
- private IconData mBluetoothData;
- private int mBluetoothHeadsetState;
- private boolean mBluetoothA2dpConnected;
- private int mBluetoothPbapState;
- private boolean mBluetoothEnabled;
-
- // wifi
- private static final int[] sWifiSignalImages = new int[] {
- com.android.internal.R.drawable.stat_sys_wifi_signal_1,
- com.android.internal.R.drawable.stat_sys_wifi_signal_2,
- com.android.internal.R.drawable.stat_sys_wifi_signal_3,
- com.android.internal.R.drawable.stat_sys_wifi_signal_4,
- };
- private static final int sWifiTemporarilyNotConnectedImage =
- com.android.internal.R.drawable.stat_sys_wifi_signal_0;
-
- private int mLastWifiSignalLevel = -1;
- private boolean mIsWifiConnected = false;
- private IBinder mWifiIcon;
- private IconData mWifiData;
-
- // gps
- private IBinder mGpsIcon;
- private IconData mGpsEnabledIconData;
- private IconData mGpsFixIconData;
-
- // alarm clock
- // Icon lit when clock is set
- private IBinder mAlarmClockIcon;
- private IconData mAlarmClockIconData;
-
- // sync state
- // If sync is active the SyncActive icon is displayed. If sync is not active but
- // sync is failing the SyncFailing icon is displayed. Otherwise neither are displayed.
- private IBinder mSyncActiveIcon;
- private IBinder mSyncFailingIcon;
-
- // TTY mode
- // Icon lit when TTY mode is enabled
- private IBinder mTTYModeIcon;
- private IconData mTTYModeEnableIconData;
-
- // Cdma Roaming Indicator, ERI
- private IBinder mCdmaRoamingIndicatorIcon;
- private IconData mCdmaRoamingIndicatorIconData;
-
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_TIME_TICK)) {
- updateClock();
- }
- else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
- updateClock();
- }
- else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- updateBattery(intent);
- }
- else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
- updateClock();
- }
- else if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
- String tz = intent.getStringExtra("time-zone");
- mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz));
- if (mClockFormat != null) {
- mClockFormat.setTimeZone(mCalendar.getTimeZone());
- }
- updateClock();
- }
- else if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
- updateAlarm(intent);
- }
- else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
- updateSyncState(intent);
- }
- else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
- onBatteryLow(intent);
- }
- else if (action.equals(Intent.ACTION_BATTERY_OKAY)
- || action.equals(Intent.ACTION_POWER_CONNECTED)) {
- onBatteryOkay(intent);
- }
- else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
- action.equals(BluetoothHeadset.ACTION_STATE_CHANGED) ||
- action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED) ||
- action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
- updateBluetooth(intent);
- }
- else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) ||
- action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) ||
- action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- updateWifi(intent);
- }
- else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) ||
- action.equals(LocationManager.GPS_FIX_CHANGE_ACTION)) {
- updateGps(intent);
- }
- else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
- action.equals(AudioManager.VIBRATE_SETTING_CHANGED_ACTION)) {
- updateVolume();
- }
- else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
- updateSimState(intent);
- }
- else if (action.equals(TtyIntent.TTY_ENABLED_CHANGE_ACTION)) {
- updateTTY(intent);
- }
- }
- };
-
- private StatusBarPolicy(Context context, StatusBarService service) {
- mContext = context;
- mService = service;
- mSignalStrength = new SignalStrength();
- mBatteryStats = BatteryStatsService.getService();
-
- // clock
- mCalendar = Calendar.getInstance(TimeZone.getDefault());
- mClockData = IconData.makeText("clock", "");
- mClockIcon = service.addIcon(mClockData, null);
- updateClock();
-
- // storage
- mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
- mStorageManager.registerListener(
- new com.android.server.status.StorageNotification(context));
-
- // battery
- mBatteryData = IconData.makeIcon("battery",
- null, com.android.internal.R.drawable.stat_sys_battery_unknown, 0, 0);
- mBatteryIcon = service.addIcon(mBatteryData, null);
-
- // phone_signal
- mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
- mPhoneData = IconData.makeIcon("phone_signal",
- null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
- mPhoneIcon = service.addIcon(mPhoneData, null);
-
- // register for phone state notifications.
- ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE))
- .listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_SERVICE_STATE
- | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
- | PhoneStateListener.LISTEN_CALL_STATE
- | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
- | PhoneStateListener.LISTEN_DATA_ACTIVITY);
-
- // data_connection
- mDataData = IconData.makeIcon("data_connection",
- null, com.android.internal.R.drawable.stat_sys_data_connected_g, 0, 0);
- mDataIcon = service.addIcon(mDataData, null);
- service.setIconVisibility(mDataIcon, false);
-
- // wifi
- mWifiData = IconData.makeIcon("wifi", null, sWifiSignalImages[0], 0, 0);
- mWifiIcon = service.addIcon(mWifiData, null);
- service.setIconVisibility(mWifiIcon, false);
- // wifi will get updated by the sticky intents
-
- // TTY status
- mTTYModeEnableIconData = IconData.makeIcon("tty",
- null, com.android.internal.R.drawable.stat_sys_tty_mode, 0, 0);
- mTTYModeIcon = service.addIcon(mTTYModeEnableIconData, null);
- service.setIconVisibility(mTTYModeIcon, false);
-
- // Cdma Roaming Indicator, ERI
- mCdmaRoamingIndicatorIconData = IconData.makeIcon("cdma_eri",
- null, com.android.internal.R.drawable.stat_sys_roaming_cdma_0, 0, 0);
- mCdmaRoamingIndicatorIcon = service.addIcon(mCdmaRoamingIndicatorIconData, null);
- service.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
-
- // bluetooth status
- mBluetoothData = IconData.makeIcon("bluetooth",
- null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0);
- mBluetoothIcon = service.addIcon(mBluetoothData, null);
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null) {
- mBluetoothEnabled = adapter.isEnabled();
- } else {
- mBluetoothEnabled = false;
- }
- mBluetoothA2dpConnected = false;
- mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
- mBluetoothPbapState = BluetoothPbap.STATE_DISCONNECTED;
- mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
-
- // Gps status
- mGpsEnabledIconData = IconData.makeIcon("gps",
- null, com.android.internal.R.drawable.stat_sys_gps_acquiring_anim, 0, 0);
- mGpsFixIconData = IconData.makeIcon("gps",
- null, com.android.internal.R.drawable.stat_sys_gps_on, 0, 0);
- mGpsIcon = service.addIcon(mGpsEnabledIconData, null);
- service.setIconVisibility(mGpsIcon, false);
-
- // Alarm clock
- mAlarmClockIconData = IconData.makeIcon(
- "alarm_clock",
- null, com.android.internal.R.drawable.stat_notify_alarm, 0, 0);
- mAlarmClockIcon = service.addIcon(mAlarmClockIconData, null);
- service.setIconVisibility(mAlarmClockIcon, false);
-
- // Sync state
- mSyncActiveIcon = service.addIcon(IconData.makeIcon("sync_active",
- null, R.drawable.stat_notify_sync_anim0, 0, 0), null);
- mSyncFailingIcon = service.addIcon(IconData.makeIcon("sync_failing",
- null, R.drawable.stat_notify_sync_error, 0, 0), null);
- service.setIconVisibility(mSyncActiveIcon, false);
- service.setIconVisibility(mSyncFailingIcon, false);
-
- // volume
- mVolumeData = IconData.makeIcon("volume",
- null, com.android.internal.R.drawable.stat_sys_ringer_silent, 0, 0);
- mVolumeIcon = service.addIcon(mVolumeData, null);
- service.setIconVisibility(mVolumeIcon, false);
- updateVolume();
-
- IntentFilter filter = new IntentFilter();
-
- // Register for Intent broadcasts for...
- filter.addAction(Intent.ACTION_TIME_TICK);
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_BATTERY_LOW);
- filter.addAction(Intent.ACTION_BATTERY_OKAY);
- filter.addAction(Intent.ACTION_POWER_CONNECTED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- filter.addAction(Intent.ACTION_ALARM_CHANGED);
- filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
- filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
- filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
- filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
- filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
- filter.addAction(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
- filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
- filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
- filter.addAction(LocationManager.GPS_FIX_CHANGE_ACTION);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
- mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
-
- // load config to determine if to distinguish Hspa data icon
- try {
- mHspaDataDistinguishable = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_hspa_data_distinguishable);
- } catch (Exception e) {
- mHspaDataDistinguishable = false;
- }
- }
-
- public static void installIcons(Context context, StatusBarService service) {
- sInstance = new StatusBarPolicy(context, service);
- }
-
- private final CharSequence getSmallTime() {
- boolean b24 = DateFormat.is24HourFormat(mContext);
- int res;
-
- if (b24) {
- res = R.string.twenty_four_hour_time_format;
- } else {
- res = R.string.twelve_hour_time_format;
- }
-
- final char MAGIC1 = '\uEF00';
- final char MAGIC2 = '\uEF01';
-
- SimpleDateFormat sdf;
- String format = mContext.getString(res);
- if (!format.equals(mClockFormatString)) {
- /*
- * Search for an unquoted "a" in the format string, so we can
- * add dummy characters around it to let us find it again after
- * formatting and change its size.
- */
- if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) {
- int a = -1;
- boolean quoted = false;
- for (int i = 0; i < format.length(); i++) {
- char c = format.charAt(i);
-
- if (c == '\'') {
- quoted = !quoted;
- }
-
- if (!quoted && c == 'a') {
- a = i;
- break;
- }
- }
-
- if (a >= 0) {
- // Move a back so any whitespace before the AM/PM is also in the alternate size.
- final int b = a;
- while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {
- a--;
- }
- format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
- + "a" + MAGIC2 + format.substring(b + 1);
- }
- }
-
- mClockFormat = sdf = new SimpleDateFormat(format);
- mClockFormatString = format;
- } else {
- sdf = mClockFormat;
- }
- String result = sdf.format(mCalendar.getTime());
-
- if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) {
- int magic1 = result.indexOf(MAGIC1);
- int magic2 = result.indexOf(MAGIC2);
-
- if (magic1 >= 0 && magic2 > magic1) {
- SpannableStringBuilder formatted = new SpannableStringBuilder(result);
-
- if (AM_PM_STYLE == AM_PM_STYLE_GONE) {
- formatted.delete(magic1, magic2+1);
- } else {
- if (AM_PM_STYLE == AM_PM_STYLE_SMALL) {
- CharacterStyle style = new RelativeSizeSpan(0.7f);
- formatted.setSpan(style, magic1, magic2,
- Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
- }
-
- formatted.delete(magic2, magic2 + 1);
- formatted.delete(magic1, magic1 + 1);
- }
-
- return formatted;
- }
- }
-
- return result;
- }
-
- private final void updateClock() {
- mCalendar.setTimeInMillis(System.currentTimeMillis());
- mClockData.text = getSmallTime();
- mService.updateIcon(mClockIcon, mClockData, null);
- }
-
- private final void updateAlarm(Intent intent) {
- boolean alarmSet = intent.getBooleanExtra("alarmSet", false);
- mService.setIconVisibility(mAlarmClockIcon, alarmSet);
- }
-
- private final void updateSyncState(Intent intent) {
- boolean isActive = intent.getBooleanExtra("active", false);
- boolean isFailing = intent.getBooleanExtra("failing", false);
- mService.setIconVisibility(mSyncActiveIcon, isActive);
- // Don't display sync failing icon: BUG 1297963 Set sync error timeout to "never"
- //mService.setIconVisibility(mSyncFailingIcon, isFailing && !isActive);
- }
-
- private final void updateBattery(Intent intent) {
- mBatteryData.iconId = intent.getIntExtra("icon-small", 0);
- mBatteryData.iconLevel = intent.getIntExtra("level", 0);
- mService.updateIcon(mBatteryIcon, mBatteryData, null);
-
- boolean plugged = intent.getIntExtra("plugged", 0) != 0;
- int level = intent.getIntExtra("level", -1);
- if (false) {
- Slog.d(TAG, "updateBattery level=" + level
- + " plugged=" + plugged
- + " mBatteryPlugged=" + mBatteryPlugged
- + " mBatteryLevel=" + mBatteryLevel
- + " mBatteryFirst=" + mBatteryFirst);
- }
-
- boolean oldPlugged = mBatteryPlugged;
-
- mBatteryPlugged = plugged;
- mBatteryLevel = level;
-
- if (mBatteryFirst) {
- mBatteryFirst = false;
- }
- /*
- * No longer showing the battery view because it draws attention away
- * from the USB storage notification. We could still show it when
- * connected to a brick, but that could lead to the user into thinking
- * the device does not charge when plugged into USB (since he/she would
- * not see the same battery screen on USB as he sees on brick).
- */
- /* else {
- if (plugged && !oldPlugged) {
- showBatteryView();
- }
- }
- */
- if (false) {
- Slog.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
- }
- }
-
- private void onBatteryLow(Intent intent) {
- if (SHOW_LOW_BATTERY_WARNING) {
- if (false) {
- Slog.d(TAG, "mPhoneState=" + mPhoneState
- + " mLowBatteryDialog=" + mLowBatteryDialog
- + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
- }
-
- if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
- showLowBatteryWarning();
- } else {
- mBatteryShowLowOnEndCall = true;
- }
- }
- }
-
- private void onBatteryOkay(Intent intent) {
- if (mLowBatteryDialog != null
- && SHOW_LOW_BATTERY_WARNING) {
- mLowBatteryDialog.dismiss();
- mBatteryShowLowOnEndCall = false;
- }
- }
-
- private void showBatteryView() {
- closeLastBatteryView();
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- }
-
- int level = mBatteryLevel;
-
- View v = View.inflate(mContext, com.android.internal.R.layout.battery_status, null);
- mBatteryView = v;
- int pixelFormat = PixelFormat.TRANSLUCENT;
- Drawable bg = v.getBackground();
- if (bg != null) {
- pixelFormat = bg.getOpacity();
- }
-
- int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_sf_slowBlur)) {
- flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
- }
-
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_TOAST,
- flags, pixelFormat);
-
- // Get the dim amount from the theme
- TypedArray a = mContext.obtainStyledAttributes(
- com.android.internal.R.styleable.Theme);
- lp.dimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
- a.recycle();
-
- lp.setTitle("Battery");
-
- TextView levelTextView = (TextView)v.findViewById(com.android.internal.R.id.level_percent);
- levelTextView.setText(mContext.getString(
- com.android.internal.R.string.battery_status_text_percent_format, level));
-
- setBatteryLevel(v, com.android.internal.R.id.spacer, 100-level, 0, 0);
- setBatteryLevel(v, com.android.internal.R.id.level, level,
- com.android.internal.R.drawable.battery_charge_fill, level);
-
- WindowManagerImpl.getDefault().addView(v, lp);
-
- scheduleCloseBatteryView();
- }
-
- private void setBatteryLevel(View parent, int id, int height, int background, int level) {
- ImageView v = (ImageView)parent.findViewById(id);
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)v.getLayoutParams();
- lp.weight = height;
- if (background != 0) {
- v.setBackgroundResource(background);
- Drawable bkg = v.getBackground();
- bkg.setLevel(level);
- }
- }
-
- private void showLowBatteryWarning() {
- closeLastBatteryView();
-
- // Show exact battery level.
- CharSequence levelText = mContext.getString(
- com.android.internal.R.string.battery_low_percent_format, mBatteryLevel);
-
- if (mBatteryLevelTextView != null) {
- mBatteryLevelTextView.setText(levelText);
- } else {
- View v = View.inflate(mContext, com.android.internal.R.layout.battery_low, null);
- mBatteryLevelTextView=(TextView)v.findViewById(com.android.internal.R.id.level_percent);
-
- mBatteryLevelTextView.setText(levelText);
-
- AlertDialog.Builder b = new AlertDialog.Builder(mContext);
- b.setCancelable(true);
- b.setTitle(com.android.internal.R.string.battery_low_title);
- b.setView(v);
- b.setIcon(android.R.drawable.ic_dialog_alert);
- b.setPositiveButton(android.R.string.ok, null);
-
- final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_NO_HISTORY);
- if (intent.resolveActivity(mContext.getPackageManager()) != null) {
- b.setNegativeButton(com.android.internal.R.string.battery_low_why,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- mContext.startActivity(intent);
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- }
- }
- });
- }
-
- AlertDialog d = b.create();
- d.setOnDismissListener(mLowBatteryListener);
- d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- d.show();
- mLowBatteryDialog = d;
- }
-
- final ContentResolver cr = mContext.getContentResolver();
- if (Settings.System.getInt(cr,
- Settings.System.POWER_SOUNDS_ENABLED, 1) == 1)
- {
- final String soundPath = Settings.System.getString(cr,
- Settings.System.LOW_BATTERY_SOUND);
- if (soundPath != null) {
- final Uri soundUri = Uri.parse("file://" + soundPath);
- if (soundUri != null) {
- final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
- if (sfx != null) {
- sfx.setStreamType(AudioManager.STREAM_SYSTEM);
- sfx.play();
- }
- }
- }
- }
- }
-
- private final void updateCallState(int state) {
- mPhoneState = state;
- if (false) {
- Slog.d(TAG, "mPhoneState=" + mPhoneState
- + " mLowBatteryDialog=" + mLowBatteryDialog
- + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
- }
- if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
- if (mBatteryShowLowOnEndCall) {
- if (!mBatteryPlugged) {
- showLowBatteryWarning();
- }
- mBatteryShowLowOnEndCall = false;
- }
- } else {
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- mBatteryShowLowOnEndCall = true;
- }
- }
- }
-
- private DialogInterface.OnDismissListener mLowBatteryListener
- = new DialogInterface.OnDismissListener() {
- public void onDismiss(DialogInterface dialog) {
- mLowBatteryDialog = null;
- mBatteryLevelTextView = null;
- }
- };
-
- private void scheduleCloseBatteryView() {
- Message m = mHandler.obtainMessage(EVENT_BATTERY_CLOSE);
- m.arg1 = (++mBatteryViewSequence);
- mHandler.sendMessageDelayed(m, 3000);
- }
-
- private void closeLastBatteryView() {
- if (mBatteryView != null) {
- //mBatteryView.debug();
- WindowManagerImpl.getDefault().removeView(mBatteryView);
- mBatteryView = null;
- }
- }
-
- private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- mSignalStrength = signalStrength;
- updateSignalStrength();
- }
-
- @Override
- public void onServiceStateChanged(ServiceState state) {
- mServiceState = state;
- updateSignalStrength();
- updateCdmaRoamingIcon(state);
- updateDataIcon();
- }
-
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- updateCallState(state);
- // In cdma, if a voice call is made, RSSI should switch to 1x.
- if (isCdma()) {
- updateSignalStrength();
- }
- }
-
- @Override
- public void onDataConnectionStateChanged(int state, int networkType) {
- mDataState = state;
- updateDataNetType(networkType);
- updateDataIcon();
- }
-
- @Override
- public void onDataActivity(int direction) {
- mDataActivity = direction;
- updateDataIcon();
- }
- };
-
- private final void updateSimState(Intent intent) {
- String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
- if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
- mSimState = IccCard.State.ABSENT;
- }
- else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
- mSimState = IccCard.State.READY;
- }
- else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
- final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
- if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
- mSimState = IccCard.State.PIN_REQUIRED;
- }
- else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
- mSimState = IccCard.State.PUK_REQUIRED;
- }
- else {
- mSimState = IccCard.State.NETWORK_LOCKED;
- }
- } else {
- mSimState = IccCard.State.UNKNOWN;
- }
- updateDataIcon();
- }
-
- private boolean isCdma() {
- return (mSignalStrength != null) && !mSignalStrength.isGsm();
- }
-
- private boolean isEvdo() {
- return ( (mServiceState != null)
- && ((mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
- || (mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_A)
- || (mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_B)));
- }
-
- private boolean hasService() {
- if (mServiceState != null) {
- switch (mServiceState.getState()) {
- case ServiceState.STATE_OUT_OF_SERVICE:
- case ServiceState.STATE_POWER_OFF:
- return false;
- default:
- return true;
- }
- } else {
- return false;
- }
- }
-
- private final void updateSignalStrength() {
- int iconLevel = -1;
- int[] iconList;
-
- // Display signal strength while in "emergency calls only" mode
- if (!hasService() && !mServiceState.isEmergencyOnly()) {
- //Slog.d(TAG, "updateSignalStrength: no service");
- if (Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
- mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_flightmode;
- } else {
- mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null;
- }
- mService.updateIcon(mPhoneIcon, mPhoneData, null);
- return;
- }
-
- if (!isCdma()) {
- int asu = mSignalStrength.getGsmSignalStrength();
-
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- if (asu <= 2 || asu == 99) iconLevel = 0;
- else if (asu >= 12) iconLevel = 4;
- else if (asu >= 8) iconLevel = 3;
- else if (asu >= 5) iconLevel = 2;
- else iconLevel = 1;
-
- // Though mPhone is a Manager, this call is not an IPC
- if (mPhone.isNetworkRoaming()) {
- iconList = sSignalImages_r;
- } else {
- iconList = sSignalImages;
- }
- } else {
- iconList = this.sSignalImages;
-
- // If 3G(EV) and 1x network are available than 3G should be
- // displayed, displayed RSSI should be from the EV side.
- // If a voice call is made then RSSI should switch to 1x.
- if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
- iconLevel = getEvdoLevel();
- if (false) {
- Slog.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level=" + getCdmaLevel());
- }
- } else {
- iconLevel = getCdmaLevel();
- }
- }
- mPhoneData.iconId = iconList[iconLevel];
- mService.updateIcon(mPhoneIcon, mPhoneData, null);
- }
-
- private int getCdmaLevel() {
- final int cdmaDbm = mSignalStrength.getCdmaDbm();
- final int cdmaEcio = mSignalStrength.getCdmaEcio();
- int levelDbm = 0;
- int levelEcio = 0;
-
- if (cdmaDbm >= -75) levelDbm = 4;
- else if (cdmaDbm >= -85) levelDbm = 3;
- else if (cdmaDbm >= -95) levelDbm = 2;
- else if (cdmaDbm >= -100) levelDbm = 1;
- else levelDbm = 0;
-
- // Ec/Io are in dB*10
- if (cdmaEcio >= -90) levelEcio = 4;
- else if (cdmaEcio >= -110) levelEcio = 3;
- else if (cdmaEcio >= -130) levelEcio = 2;
- else if (cdmaEcio >= -150) levelEcio = 1;
- else levelEcio = 0;
-
- return (levelDbm < levelEcio) ? levelDbm : levelEcio;
- }
-
- private int getEvdoLevel() {
- int evdoDbm = mSignalStrength.getEvdoDbm();
- int evdoSnr = mSignalStrength.getEvdoSnr();
- int levelEvdoDbm = 0;
- int levelEvdoSnr = 0;
-
- if (evdoDbm >= -65) levelEvdoDbm = 4;
- else if (evdoDbm >= -75) levelEvdoDbm = 3;
- else if (evdoDbm >= -90) levelEvdoDbm = 2;
- else if (evdoDbm >= -105) levelEvdoDbm = 1;
- else levelEvdoDbm = 0;
-
- if (evdoSnr >= 7) levelEvdoSnr = 4;
- else if (evdoSnr >= 5) levelEvdoSnr = 3;
- else if (evdoSnr >= 3) levelEvdoSnr = 2;
- else if (evdoSnr >= 1) levelEvdoSnr = 1;
- else levelEvdoSnr = 0;
-
- return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
- }
-
- private final void updateDataNetType(int net) {
- switch (net) {
- case TelephonyManager.NETWORK_TYPE_EDGE:
- mDataIconList = sDataNetType_e;
- break;
- case TelephonyManager.NETWORK_TYPE_UMTS:
- mDataIconList = sDataNetType_3g;
- break;
- case TelephonyManager.NETWORK_TYPE_HSDPA:
- case TelephonyManager.NETWORK_TYPE_HSUPA:
- case TelephonyManager.NETWORK_TYPE_HSPA:
- if (mHspaDataDistinguishable) {
- mDataIconList = sDataNetType_h;
- } else {
- mDataIconList = sDataNetType_3g;
- }
- break;
- case TelephonyManager.NETWORK_TYPE_CDMA:
- // display 1xRTT for IS95A/B
- mDataIconList = this.sDataNetType_1x;
- break;
- case TelephonyManager.NETWORK_TYPE_1xRTT:
- mDataIconList = this.sDataNetType_1x;
- break;
- case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
- case TelephonyManager.NETWORK_TYPE_EVDO_A:
- case TelephonyManager.NETWORK_TYPE_EVDO_B:
- mDataIconList = sDataNetType_3g;
- break;
- default:
- mDataIconList = sDataNetType_g;
- break;
- }
- }
-
- private final void updateDataIcon() {
- int iconId;
- boolean visible = true;
-
- if (!isCdma()) {
- // GSM case, we have to check also the sim state
- if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) {
- if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
- switch (mDataActivity) {
- case TelephonyManager.DATA_ACTIVITY_IN:
- iconId = mDataIconList[1];
- break;
- case TelephonyManager.DATA_ACTIVITY_OUT:
- iconId = mDataIconList[2];
- break;
- case TelephonyManager.DATA_ACTIVITY_INOUT:
- iconId = mDataIconList[3];
- break;
- default:
- iconId = mDataIconList[0];
- break;
- }
- mDataData.iconId = iconId;
- mService.updateIcon(mDataIcon, mDataData, null);
- } else {
- visible = false;
- }
- } else {
- mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim;
- mService.updateIcon(mDataIcon, mDataData, null);
- }
- } else {
- // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT
- if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
- switch (mDataActivity) {
- case TelephonyManager.DATA_ACTIVITY_IN:
- iconId = mDataIconList[1];
- break;
- case TelephonyManager.DATA_ACTIVITY_OUT:
- iconId = mDataIconList[2];
- break;
- case TelephonyManager.DATA_ACTIVITY_INOUT:
- iconId = mDataIconList[3];
- break;
- case TelephonyManager.DATA_ACTIVITY_DORMANT:
- default:
- iconId = mDataIconList[0];
- break;
- }
- mDataData.iconId = iconId;
- mService.updateIcon(mDataIcon, mDataData, null);
- } else {
- visible = false;
- }
- }
-
- long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible);
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- if (mDataIconVisible != visible) {
- mService.setIconVisibility(mDataIcon, visible);
- mDataIconVisible = visible;
- }
- }
-
- private final void updateVolume() {
- AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- final int ringerMode = audioManager.getRingerMode();
- final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
- ringerMode == AudioManager.RINGER_MODE_VIBRATE;
- final int iconId = audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)
- ? com.android.internal.R.drawable.stat_sys_ringer_vibrate
- : com.android.internal.R.drawable.stat_sys_ringer_silent;
-
- if (visible) {
- mVolumeData.iconId = iconId;
- mService.updateIcon(mVolumeIcon, mVolumeData, null);
- }
- if (visible != mVolumeVisible) {
- mService.setIconVisibility(mVolumeIcon, visible);
- mVolumeVisible = visible;
- }
- }
-
- private final void updateBluetooth(Intent intent) {
- int iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth;
- String action = intent.getAction();
- if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
- int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
- mBluetoothEnabled = state == BluetoothAdapter.STATE_ON;
- } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
- mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
- BluetoothHeadset.STATE_ERROR);
- } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
- BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
- if (a2dp.getConnectedSinks().size() != 0) {
- mBluetoothA2dpConnected = true;
- } else {
- mBluetoothA2dpConnected = false;
- }
- } else if (action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
- mBluetoothPbapState = intent.getIntExtra(BluetoothPbap.PBAP_STATE,
- BluetoothPbap.STATE_DISCONNECTED);
- } else {
- return;
- }
-
- if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || mBluetoothA2dpConnected ||
- mBluetoothPbapState == BluetoothPbap.STATE_CONNECTED) {
- iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth_connected;
- }
-
- mBluetoothData.iconId = iconId;
- mService.updateIcon(mBluetoothIcon, mBluetoothData, null);
- mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
- }
-
- private final void updateWifi(Intent intent) {
- final String action = intent.getAction();
- if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-
- final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
-
- if (!enabled) {
- // If disabled, hide the icon. (We show icon when connected.)
- mService.setIconVisibility(mWifiIcon, false);
- }
-
- } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
- final boolean enabled = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED,
- false);
- if (!enabled) {
- mService.setIconVisibility(mWifiIcon, false);
- }
- } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-
- final NetworkInfo networkInfo = (NetworkInfo)
- intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-
- int iconId;
- if (networkInfo != null && networkInfo.isConnected()) {
- mIsWifiConnected = true;
- if (mLastWifiSignalLevel == -1) {
- iconId = sWifiSignalImages[0];
- } else {
- iconId = sWifiSignalImages[mLastWifiSignalLevel];
- }
-
- // Show the icon since wi-fi is connected
- mService.setIconVisibility(mWifiIcon, true);
-
- } else {
- mLastWifiSignalLevel = -1;
- mIsWifiConnected = false;
- iconId = sWifiSignalImages[0];
-
- // Hide the icon since we're not connected
- mService.setIconVisibility(mWifiIcon, false);
- }
-
- mWifiData.iconId = iconId;
- mService.updateIcon(mWifiIcon, mWifiData, null);
- } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
- int newSignalLevel = WifiManager.calculateSignalLevel(newRssi,
- sWifiSignalImages.length);
- if (newSignalLevel != mLastWifiSignalLevel) {
- mLastWifiSignalLevel = newSignalLevel;
- if (mIsWifiConnected) {
- mWifiData.iconId = sWifiSignalImages[newSignalLevel];
- } else {
- mWifiData.iconId = sWifiTemporarilyNotConnectedImage;
- }
- mService.updateIcon(mWifiIcon, mWifiData, null);
- }
- }
- }
-
- private final void updateGps(Intent intent) {
- final String action = intent.getAction();
- final boolean enabled = intent.getBooleanExtra(LocationManager.EXTRA_GPS_ENABLED, false);
-
- if (action.equals(LocationManager.GPS_FIX_CHANGE_ACTION) && enabled) {
- // GPS is getting fixes
- mService.updateIcon(mGpsIcon, mGpsFixIconData, null);
- mService.setIconVisibility(mGpsIcon, true);
- } else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
- // GPS is off
- mService.setIconVisibility(mGpsIcon, false);
- } else {
- // GPS is on, but not receiving fixes
- mService.updateIcon(mGpsIcon, mGpsEnabledIconData, null);
- mService.setIconVisibility(mGpsIcon, true);
- }
- }
-
- private final void updateTTY(Intent intent) {
- final String action = intent.getAction();
- final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
-
- if (false) Slog.v(TAG, "updateTTY: enabled: " + enabled);
-
- if (enabled) {
- // TTY is on
- if (false) Slog.v(TAG, "updateTTY: set TTY on");
- mService.updateIcon(mTTYModeIcon, mTTYModeEnableIconData, null);
- mService.setIconVisibility(mTTYModeIcon, true);
- } else {
- // TTY is off
- if (false) Slog.v(TAG, "updateTTY: set TTY off");
- mService.setIconVisibility(mTTYModeIcon, false);
- }
- }
-
- private final void updateCdmaRoamingIcon(ServiceState state) {
- if (!hasService()) {
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
- return;
- }
-
- if (!isCdma()) {
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
- return;
- }
-
- int[] iconList = sRoamingIndicatorImages_cdma;
- int iconIndex = state.getCdmaEriIconIndex();
- int iconMode = state.getCdmaEriIconMode();
-
- if (iconIndex == -1) {
- Slog.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update");
- return;
- }
-
- if (iconMode == -1) {
- Slog.e(TAG, "getCdmeEriIconMode returned null, skipping ERI icon update");
- return;
- }
-
- if (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) {
- if (false) Slog.v(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon");
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
- return;
- }
-
- switch (iconMode) {
- case EriInfo.ROAMING_ICON_MODE_NORMAL:
- mCdmaRoamingIndicatorIconData.iconId = iconList[iconIndex];
- mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
- break;
- case EriInfo.ROAMING_ICON_MODE_FLASH:
- mCdmaRoamingIndicatorIconData.iconId =
- com.android.internal.R.drawable.stat_sys_roaming_cdma_flash;
- mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
- break;
-
- }
- mService.updateIcon(mPhoneIcon, mPhoneData, null);
- }
-
-
- private class StatusBarHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_BATTERY_CLOSE:
- if (msg.arg1 == mBatteryViewSequence) {
- closeLastBatteryView();
- }
- break;
- }
- }
- }
-}
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
deleted file mode 100644
index 93c8d34..0000000
--- a/services/java/com/android/server/status/StatusBarService.java
+++ /dev/null
@@ -1,1881 +0,0 @@
-/*
- * Copyright (C) 2007 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.status;
-
-import com.android.internal.R;
-import com.android.internal.util.CharSequences;
-
-import android.app.ActivityManagerNative;
-import android.app.Dialog;
-import android.app.IStatusBar;
-import android.app.PendingIntent;
-import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.provider.Telephony;
-import android.util.Slog;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.LinearLayout;
-import android.widget.RemoteViews;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.widget.FrameLayout;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Set;
-
-
-/**
- * The public (ok, semi-public) service for the status bar.
- * <p>
- * This interesting thing to note about this class is that most of the methods that
- * are called from other classes just post a message, and everything else is batched
- * and coalesced into a series of calls to methods that all start with "perform."
- * There are two reasons for this. The first is that some of the methods (activate/deactivate)
- * are on IStatusBar, so they're called from the thread pool and they need to make their
- * way onto the UI thread. The second is that the message queue is stopped while animations
- * are happening in order to make for smoother transitions.
- * <p>
- * Each icon is either an icon or an icon and a notification. They're treated mostly
- * separately throughout the code, although they both use the same key, which is assigned
- * when they are created.
- */
-public class StatusBarService extends IStatusBar.Stub
-{
- static final String TAG = "StatusBar";
- static final boolean SPEW = false;
-
- static final int EXPANDED_LEAVE_ALONE = -10000;
- static final int EXPANDED_FULL_OPEN = -10001;
-
- private static final int MSG_ANIMATE = 1000;
- private static final int MSG_ANIMATE_REVEAL = 1001;
-
- private static final int OP_ADD_ICON = 1;
- private static final int OP_UPDATE_ICON = 2;
- private static final int OP_REMOVE_ICON = 3;
- private static final int OP_SET_VISIBLE = 4;
- private static final int OP_EXPAND = 5;
- private static final int OP_TOGGLE = 6;
- private static final int OP_DISABLE = 7;
- private class PendingOp {
- IBinder key;
- int code;
- IconData iconData;
- NotificationData notificationData;
- boolean visible;
- int integer;
- }
-
- private class DisableRecord implements IBinder.DeathRecipient {
- String pkg;
- int what;
- IBinder token;
-
- public void binderDied() {
- Slog.i(TAG, "binder died for pkg=" + pkg);
- disable(0, token, pkg);
- token.unlinkToDeath(this, 0);
- }
- }
-
- public interface NotificationCallbacks {
- void onSetDisabled(int status);
- void onClearAll();
- void onNotificationClick(String pkg, String tag, int id);
- void onPanelRevealed();
- }
-
- private class ExpandedDialog extends Dialog {
- ExpandedDialog(Context context) {
- super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_BACK:
- if (!down) {
- StatusBarService.this.deactivate();
- }
- return true;
- }
- return super.dispatchKeyEvent(event);
- }
- }
-
- final Context mContext;
- final Display mDisplay;
- StatusBarView mStatusBarView;
- int mPixelFormat;
- H mHandler = new H();
- Object mQueueLock = new Object();
- ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>();
- NotificationCallbacks mNotificationCallbacks;
-
- // All accesses to mIconMap and mNotificationData are syncronized on those objects,
- // but this is only so dump() can work correctly. Modifying these outside of the UI
- // thread will not work, there are places in the code that unlock and reaquire between
- // reads and require them to not be modified.
-
- // icons
- HashMap<IBinder,StatusBarIcon> mIconMap = new HashMap<IBinder,StatusBarIcon>();
- ArrayList<StatusBarIcon> mIconList = new ArrayList<StatusBarIcon>();
- String[] mRightIconSlots;
- StatusBarIcon[] mRightIcons;
- LinearLayout mIcons;
- IconMerger mNotificationIcons;
- LinearLayout mStatusIcons;
- StatusBarIcon mMoreIcon;
- private UninstallReceiver mUninstallReceiver;
-
- // expanded notifications
- NotificationViewList mNotificationData = new NotificationViewList();
- Dialog mExpandedDialog;
- ExpandedView mExpandedView;
- WindowManager.LayoutParams mExpandedParams;
- ScrollView mScrollView;
- View mNotificationLinearLayout;
- TextView mOngoingTitle;
- LinearLayout mOngoingItems;
- TextView mLatestTitle;
- LinearLayout mLatestItems;
- TextView mNoNotificationsTitle;
- TextView mSpnLabel;
- TextView mPlmnLabel;
- TextView mClearButton;
- View mExpandedContents;
- CloseDragHandle mCloseView;
- int[] mPositionTmp = new int[2];
- boolean mExpanded;
- boolean mExpandedVisible;
-
- // the date view
- DateView mDateView;
-
- // the tracker view
- TrackingView mTrackingView;
- WindowManager.LayoutParams mTrackingParams;
- int mTrackingPosition; // the position of the top of the tracking view.
-
- // ticker
- private Ticker mTicker;
- private View mTickerView;
- private boolean mTicking;
-
- // Tracking finger for opening/closing.
- int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
- boolean mTracking;
- VelocityTracker mVelocityTracker;
-
- static final int ANIM_FRAME_DURATION = (1000/60);
-
- boolean mAnimating;
- long mCurAnimationTime;
- float mDisplayHeight;
- float mAnimY;
- float mAnimVel;
- float mAnimAccel;
- long mAnimLastTime;
- boolean mAnimatingReveal = false;
- int mViewDelta;
- int[] mAbsPos = new int[2];
-
- // for disabling the status bar
- ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
- int mDisabled = 0;
-
- /**
- * Construct the service, add the status bar view to the window manager
- */
- public StatusBarService(Context context) {
- mContext = context;
- mDisplay = ((WindowManager)context.getSystemService(
- Context.WINDOW_SERVICE)).getDefaultDisplay();
- makeStatusBarView(context);
- mUninstallReceiver = new UninstallReceiver();
- }
-
- public void setNotificationCallbacks(NotificationCallbacks listener) {
- mNotificationCallbacks = listener;
- }
-
- // ================================================================================
- // Constructing the view
- // ================================================================================
- private void makeStatusBarView(Context context) {
- Resources res = context.getResources();
- mRightIconSlots = res.getStringArray(com.android.internal.R.array.status_bar_icon_order);
- mRightIcons = new StatusBarIcon[mRightIconSlots.length];
-
- ExpandedView expanded = (ExpandedView)View.inflate(context,
- com.android.internal.R.layout.status_bar_expanded, null);
- expanded.mService = this;
- StatusBarView sb = (StatusBarView)View.inflate(context,
- com.android.internal.R.layout.status_bar, null);
- sb.mService = this;
-
- // figure out which pixel-format to use for the status bar.
- mPixelFormat = PixelFormat.TRANSLUCENT;
- Drawable bg = sb.getBackground();
- if (bg != null) {
- mPixelFormat = bg.getOpacity();
- }
-
- mStatusBarView = sb;
- mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
- mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
- mNotificationIcons.service = this;
- mIcons = (LinearLayout)sb.findViewById(R.id.icons);
- mTickerView = sb.findViewById(R.id.ticker);
- mDateView = (DateView)sb.findViewById(R.id.date);
-
- mExpandedDialog = new ExpandedDialog(context);
- mExpandedView = expanded;
- mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
- mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
- mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
- mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
- mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
- mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
- mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
- mSpnLabel = (TextView)expanded.findViewById(R.id.spnLabel);
- mPlmnLabel = (TextView)expanded.findViewById(R.id.plmnLabel);
- mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
- mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
-
- mOngoingTitle.setVisibility(View.GONE);
- mLatestTitle.setVisibility(View.GONE);
-
- mTicker = new MyTicker(context, sb);
-
- TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
- tickerView.mTicker = mTicker;
-
- mTrackingView = (TrackingView)View.inflate(context,
- com.android.internal.R.layout.status_bar_tracking, null);
- mTrackingView.mService = this;
- mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
- mCloseView.mService = this;
-
- mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
- // add the more icon for the notifications
- IconData moreData = IconData.makeIcon(null, context.getPackageName(),
- R.drawable.stat_notify_more, 0, 42);
- mMoreIcon = new StatusBarIcon(context, moreData, mNotificationIcons);
- mMoreIcon.view.setId(R.drawable.stat_notify_more);
- mNotificationIcons.moreIcon = mMoreIcon;
- mNotificationIcons.addView(mMoreIcon.view);
-
- // set the inital view visibility
- setAreThereNotifications();
- mDateView.setVisibility(View.INVISIBLE);
-
- // before we register for broadcasts
- mPlmnLabel.setText(R.string.lockscreen_carrier_default);
- mPlmnLabel.setVisibility(View.VISIBLE);
- mSpnLabel.setText("");
- mSpnLabel.setVisibility(View.GONE);
-
- // receive broadcasts
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
- context.registerReceiver(mBroadcastReceiver, filter);
- }
-
- public void systemReady() {
- final StatusBarView view = mStatusBarView;
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- view.getContext().getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height),
- WindowManager.LayoutParams.TYPE_STATUS_BAR,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
- mPixelFormat);
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("StatusBar");
- lp.windowAnimations = R.style.Animation_StatusBar;
-
- WindowManagerImpl.getDefault().addView(view, lp);
- }
-
- // ================================================================================
- // From IStatusBar
- // ================================================================================
- public void activate() {
- enforceExpandStatusBar();
- addPendingOp(OP_EXPAND, null, true);
- }
-
- public void deactivate() {
- enforceExpandStatusBar();
- addPendingOp(OP_EXPAND, null, false);
- }
-
- public void toggle() {
- enforceExpandStatusBar();
- addPendingOp(OP_TOGGLE, null, false);
- }
-
- public void disable(int what, IBinder token, String pkg) {
- enforceStatusBar();
- synchronized (mNotificationCallbacks) {
- // This is a little gross, but I think it's safe as long as nobody else
- // synchronizes on mNotificationCallbacks. It's important that the the callback
- // and the pending op get done in the correct order and not interleaved with
- // other calls, otherwise they'll get out of sync.
- int net;
- synchronized (mDisableRecords) {
- manageDisableListLocked(what, token, pkg);
- net = gatherDisableActionsLocked();
- mNotificationCallbacks.onSetDisabled(net);
- }
- addPendingOp(OP_DISABLE, net);
- }
- }
-
- public IBinder addIcon(String slot, String iconPackage, int iconId, int iconLevel) {
- enforceStatusBar();
- return addIcon(IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
- }
-
- public void updateIcon(IBinder key,
- String slot, String iconPackage, int iconId, int iconLevel) {
- enforceStatusBar();
- updateIcon(key, IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
- }
-
- public void removeIcon(IBinder key) {
- enforceStatusBar();
- addPendingOp(OP_REMOVE_ICON, key, null, null, -1);
- }
-
- private void enforceStatusBar() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.STATUS_BAR,
- "StatusBarService");
- }
-
- private void enforceExpandStatusBar() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.EXPAND_STATUS_BAR,
- "StatusBarService");
- }
-
- // ================================================================================
- // Can be called from any thread
- // ================================================================================
- public IBinder addIcon(IconData data, NotificationData n) {
- int slot;
- // assert early-on if they using a slot that doesn't exist.
- if (data != null && n == null) {
- slot = getRightIconIndex(data.slot);
- if (slot < 0) {
- throw new SecurityException("invalid status bar icon slot: "
- + (data.slot != null ? "'" + data.slot + "'" : "null"));
- }
- } else {
- slot = -1;
- }
- IBinder key = new Binder();
- addPendingOp(OP_ADD_ICON, key, data, n, -1);
- return key;
- }
-
- public void updateIcon(IBinder key, IconData data, NotificationData n) {
- addPendingOp(OP_UPDATE_ICON, key, data, n, -1);
- }
-
- public void setIconVisibility(IBinder key, boolean visible) {
- addPendingOp(OP_SET_VISIBLE, key, visible);
- }
-
- private void addPendingOp(int code, IBinder key, IconData data, NotificationData n, int i) {
- synchronized (mQueueLock) {
- PendingOp op = new PendingOp();
- op.key = key;
- op.code = code;
- op.iconData = data == null ? null : data.clone();
- op.notificationData = n;
- op.integer = i;
- mQueue.add(op);
- if (mQueue.size() == 1) {
- mHandler.sendEmptyMessage(2);
- }
- }
- }
-
- private void addPendingOp(int code, IBinder key, boolean visible) {
- synchronized (mQueueLock) {
- PendingOp op = new PendingOp();
- op.key = key;
- op.code = code;
- op.visible = visible;
- mQueue.add(op);
- if (mQueue.size() == 1) {
- mHandler.sendEmptyMessage(1);
- }
- }
- }
-
- private void addPendingOp(int code, int integer) {
- synchronized (mQueueLock) {
- PendingOp op = new PendingOp();
- op.code = code;
- op.integer = integer;
- mQueue.add(op);
- if (mQueue.size() == 1) {
- mHandler.sendEmptyMessage(1);
- }
- }
- }
-
- // lock on mDisableRecords
- void manageDisableListLocked(int what, IBinder token, String pkg) {
- if (SPEW) {
- Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what)
- + " pkg=" + pkg);
- }
- // update the list
- synchronized (mDisableRecords) {
- final int N = mDisableRecords.size();
- DisableRecord tok = null;
- int i;
- for (i=0; i<N; i++) {
- DisableRecord t = mDisableRecords.get(i);
- if (t.token == token) {
- tok = t;
- break;
- }
- }
- if (what == 0 || !token.isBinderAlive()) {
- if (tok != null) {
- mDisableRecords.remove(i);
- tok.token.unlinkToDeath(tok, 0);
- }
- } else {
- if (tok == null) {
- tok = new DisableRecord();
- try {
- token.linkToDeath(tok, 0);
- }
- catch (RemoteException ex) {
- return; // give up
- }
- mDisableRecords.add(tok);
- }
- tok.what = what;
- tok.token = token;
- tok.pkg = pkg;
- }
- }
- }
-
- // lock on mDisableRecords
- int gatherDisableActionsLocked() {
- final int N = mDisableRecords.size();
- // gather the new net flags
- int net = 0;
- for (int i=0; i<N; i++) {
- net |= mDisableRecords.get(i).what;
- }
- return net;
- }
-
- private int getRightIconIndex(String slot) {
- final int N = mRightIconSlots.length;
- for (int i=0; i<N; i++) {
- if (mRightIconSlots[i].equals(slot)) {
- return i;
- }
- }
- return -1;
- }
-
- // ================================================================================
- // Always called from UI thread
- // ================================================================================
- /**
- * All changes to the status bar and notifications funnel through here and are batched.
- */
- private class H extends Handler {
- public void handleMessage(Message m) {
- if (m.what == MSG_ANIMATE) {
- doAnimation();
- return;
- }
- if (m.what == MSG_ANIMATE_REVEAL) {
- doRevealAnimation();
- return;
- }
-
- ArrayList<PendingOp> queue;
- synchronized (mQueueLock) {
- queue = mQueue;
- mQueue = new ArrayList<PendingOp>();
- }
-
- boolean wasExpanded = mExpanded;
-
- // for each one in the queue, find all of the ones with the same key
- // and collapse that down into a final op and/or call to setVisibility, etc
- boolean expand = wasExpanded;
- boolean doExpand = false;
- boolean doDisable = false;
- int disableWhat = 0;
- int N = queue.size();
- while (N > 0) {
- PendingOp op = queue.get(0);
- boolean doOp = false;
- boolean visible = false;
- boolean doVisibility = false;
- if (op.code == OP_SET_VISIBLE) {
- doVisibility = true;
- visible = op.visible;
- }
- else if (op.code == OP_EXPAND) {
- doExpand = true;
- expand = op.visible;
- }
- else if (op.code == OP_TOGGLE) {
- doExpand = true;
- expand = !expand;
- }
- else {
- doOp = true;
- }
-
- if (alwaysHandle(op.code)) {
- // coalesce these
- for (int i=1; i<N; i++) {
- PendingOp o = queue.get(i);
- if (!alwaysHandle(o.code) && o.key == op.key) {
- if (o.code == OP_SET_VISIBLE) {
- visible = o.visible;
- doVisibility = true;
- }
- else if (o.code == OP_EXPAND) {
- expand = o.visible;
- doExpand = true;
- }
- else {
- op.code = o.code;
- op.iconData = o.iconData;
- op.notificationData = o.notificationData;
- }
- queue.remove(i);
- i--;
- N--;
- }
- }
- }
-
- queue.remove(0);
- N--;
-
- if (doOp) {
- switch (op.code) {
- case OP_ADD_ICON:
- case OP_UPDATE_ICON:
- performAddUpdateIcon(op.key, op.iconData, op.notificationData);
- break;
- case OP_REMOVE_ICON:
- performRemoveIcon(op.key);
- break;
- case OP_DISABLE:
- doDisable = true;
- disableWhat = op.integer;
- break;
- }
- }
- if (doVisibility && op.code != OP_REMOVE_ICON) {
- performSetIconVisibility(op.key, visible);
- }
- }
-
- if (queue.size() != 0) {
- throw new RuntimeException("Assertion failed: queue.size=" + queue.size());
- }
- if (doExpand) {
- // this is last so that we capture all of the pending changes before doing it
- if (expand) {
- animateExpand();
- } else {
- animateCollapse();
- }
- }
- if (doDisable) {
- performDisableActions(disableWhat);
- }
- }
- }
-
- private boolean alwaysHandle(int code) {
- return code == OP_DISABLE;
- }
-
- /* private */ void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
- throws StatusBarException {
- if (SPEW) {
- Slog.d(TAG, "performAddUpdateIcon icon=" + data + " notification=" + n + " key=" + key);
- }
- // notification
- if (n != null) {
- StatusBarNotification notification = getNotification(key);
- NotificationData oldData = null;
- if (notification == null) {
- // add
- notification = new StatusBarNotification();
- notification.key = key;
- notification.data = n;
- synchronized (mNotificationData) {
- mNotificationData.add(notification);
- }
- addNotificationView(notification);
- setAreThereNotifications();
- } else {
- // update
- oldData = notification.data;
- notification.data = n;
- updateNotificationView(notification, oldData);
- }
- // Show the ticker if one is requested, and the text is different
- // than the currently displayed ticker. Also don't do this
- // until status bar window is attached to the window manager,
- // because... well, what's the point otherwise? And trying to
- // run a ticker without being attached will crash!
- if (n.tickerText != null && mStatusBarView.getWindowToken() != null
- && (oldData == null
- || oldData.tickerText == null
- || !CharSequences.equals(oldData.tickerText, n.tickerText))) {
- if (0 == (mDisabled &
- (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
- mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
- }
- }
- updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- }
-
- // icon
- synchronized (mIconMap) {
- StatusBarIcon icon = mIconMap.get(key);
- if (icon == null) {
- // add
- LinearLayout v = n == null ? mStatusIcons : mNotificationIcons;
-
- icon = new StatusBarIcon(mContext, data, v);
- mIconMap.put(key, icon);
- mIconList.add(icon);
-
- if (n == null) {
- int slotIndex = getRightIconIndex(data.slot);
- StatusBarIcon[] rightIcons = mRightIcons;
- if (rightIcons[slotIndex] == null) {
- int pos = 0;
- for (int i=mRightIcons.length-1; i>slotIndex; i--) {
- StatusBarIcon ic = rightIcons[i];
- if (ic != null) {
- pos++;
- }
- }
- rightIcons[slotIndex] = icon;
- mStatusIcons.addView(icon.view, pos);
- } else {
- Slog.e(TAG, "duplicate icon in slot " + slotIndex + "/" + data.slot);
- mIconMap.remove(key);
- mIconList.remove(icon);
- return ;
- }
- } else {
- int iconIndex = mNotificationData.getIconIndex(n);
- mNotificationIcons.addView(icon.view, iconIndex);
- }
- } else {
- if (n == null) {
- // right hand side icons -- these don't reorder
- icon.update(mContext, data);
- } else {
- // remove old
- ViewGroup parent = (ViewGroup)icon.view.getParent();
- parent.removeView(icon.view);
- // add new
- icon.update(mContext, data);
- int iconIndex = mNotificationData.getIconIndex(n);
- mNotificationIcons.addView(icon.view, iconIndex);
- }
- }
- }
- }
-
- /* private */ void performSetIconVisibility(IBinder key, boolean visible) {
- synchronized (mIconMap) {
- if (SPEW) {
- Slog.d(TAG, "performSetIconVisibility key=" + key + " visible=" + visible);
- }
- StatusBarIcon icon = mIconMap.get(key);
- icon.view.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
- }
-
- /* private */ void performRemoveIcon(IBinder key) {
- synchronized (this) {
- if (SPEW) {
- Slog.d(TAG, "performRemoveIcon key=" + key);
- }
- StatusBarIcon icon = mIconMap.remove(key);
- mIconList.remove(icon);
- if (icon != null) {
- ViewGroup parent = (ViewGroup)icon.view.getParent();
- parent.removeView(icon.view);
- int slotIndex = getRightIconIndex(icon.mData.slot);
- if (slotIndex >= 0) {
- mRightIcons[slotIndex] = null;
- }
- }
- StatusBarNotification notification = getNotification(key);
- if (notification != null) {
- removeNotificationView(notification);
- synchronized (mNotificationData) {
- mNotificationData.remove(notification);
- }
- setAreThereNotifications();
- }
- }
- }
-
- int getIconNumberForView(View v) {
- synchronized (mIconMap) {
- StatusBarIcon icon = null;
- final int N = mIconList.size();
- for (int i=0; i<N; i++) {
- StatusBarIcon ic = mIconList.get(i);
- if (ic.view == v) {
- icon = ic;
- break;
- }
- }
- if (icon != null) {
- return icon.getNumber();
- } else {
- return -1;
- }
- }
- }
-
-
- StatusBarNotification getNotification(IBinder key) {
- synchronized (mNotificationData) {
- return mNotificationData.get(key);
- }
- }
-
- View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
- public void onFocusChange(View v, boolean hasFocus) {
- // Because 'v' is a ViewGroup, all its children will be (un)selected
- // too, which allows marqueeing to work.
- v.setSelected(hasFocus);
- }
- };
-
- View makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
- NotificationData n = notification.data;
- RemoteViews remoteViews = n.contentView;
- if (remoteViews == null) {
- return null;
- }
-
- // create the row view
- LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View row = inflater.inflate(com.android.internal.R.layout.status_bar_latest_event, parent, false);
-
- // bind the click event to the content area
- ViewGroup content = (ViewGroup)row.findViewById(com.android.internal.R.id.content);
- content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- content.setOnFocusChangeListener(mFocusChangeListener);
- PendingIntent contentIntent = n.contentIntent;
- if (contentIntent != null) {
- content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
- }
-
- View child = null;
- Exception exception = null;
- try {
- child = remoteViews.apply(mContext, content);
- }
- catch (RuntimeException e) {
- exception = e;
- }
- if (child == null) {
- Slog.e(TAG, "couldn't inflate view for package " + n.pkg, exception);
- return null;
- }
- content.addView(child);
-
- row.setDrawingCacheEnabled(true);
-
- notification.view = row;
- notification.contentView = child;
-
- return row;
- }
-
- void addNotificationView(StatusBarNotification notification) {
- if (notification.view != null) {
- throw new RuntimeException("Assertion failed: notification.view="
- + notification.view);
- }
-
- LinearLayout parent = notification.data.ongoingEvent ? mOngoingItems : mLatestItems;
-
- View child = makeNotificationView(notification, parent);
- if (child == null) {
- return ;
- }
-
- int index = mNotificationData.getExpandedIndex(notification);
- parent.addView(child, index);
- }
-
- /**
- * Remove the old one and put the new one in its place.
- * @param notification the notification
- */
- void updateNotificationView(StatusBarNotification notification, NotificationData oldData) {
- NotificationData n = notification.data;
- if (oldData != null && n != null
- && n.when == oldData.when
- && n.ongoingEvent == oldData.ongoingEvent
- && n.contentView != null && oldData.contentView != null
- && n.contentView.getPackage() != null
- && oldData.contentView.getPackage() != null
- && oldData.contentView.getPackage().equals(n.contentView.getPackage())
- && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()
- && notification.view != null) {
- mNotificationData.update(notification);
- try {
- n.contentView.reapply(mContext, notification.contentView);
-
- // update the contentIntent
- ViewGroup content = (ViewGroup)notification.view.findViewById(
- com.android.internal.R.id.content);
- PendingIntent contentIntent = n.contentIntent;
- if (contentIntent != null) {
- content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
- }
- }
- catch (RuntimeException e) {
- // It failed to add cleanly. Log, and remove the view from the panel.
- Slog.w(TAG, "couldn't reapply views for package " + n.contentView.getPackage(), e);
- removeNotificationView(notification);
- }
- } else {
- mNotificationData.update(notification);
- removeNotificationView(notification);
- addNotificationView(notification);
- }
- setAreThereNotifications();
- }
-
- void removeNotificationView(StatusBarNotification notification) {
- View v = notification.view;
- if (v != null) {
- ViewGroup parent = (ViewGroup)v.getParent();
- parent.removeView(v);
- notification.view = null;
- }
- }
-
- private void setAreThereNotifications() {
- boolean ongoing = mOngoingItems.getChildCount() != 0;
- boolean latest = mLatestItems.getChildCount() != 0;
-
- if (mNotificationData.hasClearableItems()) {
- mClearButton.setVisibility(View.VISIBLE);
- } else {
- mClearButton.setVisibility(View.INVISIBLE);
- }
-
- mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
- mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
-
- if (ongoing || latest) {
- mNoNotificationsTitle.setVisibility(View.GONE);
- } else {
- mNoNotificationsTitle.setVisibility(View.VISIBLE);
- }
- }
-
- private void makeExpandedVisible() {
- if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
- if (mExpandedVisible) {
- return;
- }
- mExpandedVisible = true;
- panelSlightlyVisible(true);
-
- updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- mExpandedView.requestFocus(View.FOCUS_FORWARD);
- mTrackingView.setVisibility(View.VISIBLE);
-
- if (!mTicking) {
- setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
-
- void animateExpand() {
- if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return ;
- }
- if (mExpanded) {
- return;
- }
-
- prepareTracking(0, true);
- performFling(0, 2000.0f, true);
- }
-
- void animateCollapse() {
- if (SPEW) {
- Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
- + " mExpandedVisible=" + mExpandedVisible
- + " mExpanded=" + mExpanded
- + " mAnimating=" + mAnimating
- + " mAnimY=" + mAnimY
- + " mAnimVel=" + mAnimVel);
- }
-
- if (!mExpandedVisible) {
- return;
- }
-
- int y;
- if (mAnimating) {
- y = (int)mAnimY;
- } else {
- y = mDisplay.getHeight()-1;
- }
- // Let the fling think that we're open so it goes in the right direction
- // and doesn't try to re-open the windowshade.
- mExpanded = true;
- prepareTracking(y, false);
- performFling(y, -2000.0f, true);
- }
-
- void performExpand() {
- if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return ;
- }
- if (mExpanded) {
- return;
- }
-
- // It seems strange to sometimes not expand...
- if (false) {
- synchronized (mNotificationData) {
- if (mNotificationData.size() == 0) {
- return;
- }
- }
- }
-
- mExpanded = true;
- makeExpandedVisible();
- updateExpandedViewPos(EXPANDED_FULL_OPEN);
-
- if (false) postStartTracing();
- }
-
- void performCollapse() {
- if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
- + " mExpandedVisible=" + mExpandedVisible);
-
- if (!mExpandedVisible) {
- return;
- }
- mExpandedVisible = false;
- panelSlightlyVisible(false);
- mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- mTrackingView.setVisibility(View.GONE);
-
- if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
- setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
- }
- setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
-
- if (!mExpanded) {
- return;
- }
- mExpanded = false;
- }
-
- void doAnimation() {
- if (mAnimating) {
- if (SPEW) Slog.d(TAG, "doAnimation");
- if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
- incrementAnim();
- if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY);
- if (mAnimY >= mDisplay.getHeight()-1) {
- if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
- mAnimating = false;
- updateExpandedViewPos(EXPANDED_FULL_OPEN);
- performExpand();
- }
- else if (mAnimY < mStatusBarView.getHeight()) {
- if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
- mAnimating = false;
- updateExpandedViewPos(0);
- performCollapse();
- }
- else {
- updateExpandedViewPos((int)mAnimY);
- mCurAnimationTime += ANIM_FRAME_DURATION;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
- }
- }
- }
-
- void stopTracking() {
- mTracking = false;
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- void incrementAnim() {
- long now = SystemClock.uptimeMillis();
- float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s
- final float y = mAnimY;
- final float v = mAnimVel; // px/s
- final float a = mAnimAccel; // px/s/s
- mAnimY = y + (v*t) + (0.5f*a*t*t); // px
- mAnimVel = v + (a*t); // px/s
- mAnimLastTime = now; // ms
- //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
- // + " mAnimAccel=" + mAnimAccel);
- }
-
- void doRevealAnimation() {
- final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
- if (mAnimatingReveal && mAnimating && mAnimY < h) {
- incrementAnim();
- if (mAnimY >= h) {
- mAnimY = h;
- updateExpandedViewPos((int)mAnimY);
- } else {
- updateExpandedViewPos((int)mAnimY);
- mCurAnimationTime += ANIM_FRAME_DURATION;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
- mCurAnimationTime);
- }
- }
- }
-
- void prepareTracking(int y, boolean opening) {
- mTracking = true;
- mVelocityTracker = VelocityTracker.obtain();
- if (opening) {
- mAnimAccel = 2000.0f;
- mAnimVel = 200;
- mAnimY = mStatusBarView.getHeight();
- updateExpandedViewPos((int)mAnimY);
- mAnimating = true;
- mAnimatingReveal = true;
- mHandler.removeMessages(MSG_ANIMATE);
- mHandler.removeMessages(MSG_ANIMATE_REVEAL);
- long now = SystemClock.uptimeMillis();
- mAnimLastTime = now;
- mCurAnimationTime = now + ANIM_FRAME_DURATION;
- mAnimating = true;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
- mCurAnimationTime);
- makeExpandedVisible();
- } else {
- // it's open, close it?
- if (mAnimating) {
- mAnimating = false;
- mHandler.removeMessages(MSG_ANIMATE);
- }
- updateExpandedViewPos(y + mViewDelta);
- }
- }
-
- void performFling(int y, float vel, boolean always) {
- mAnimatingReveal = false;
- mDisplayHeight = mDisplay.getHeight();
-
- mAnimY = y;
- mAnimVel = vel;
-
- //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
-
- if (mExpanded) {
- if (!always && (
- vel > 200.0f
- || (y > (mDisplayHeight-25) && vel > -200.0f))) {
- // We are expanded, but they didn't move sufficiently to cause
- // us to retract. Animate back to the expanded position.
- mAnimAccel = 2000.0f;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are expanded and are now going to animate away.
- mAnimAccel = -2000.0f;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- } else {
- if (always || (
- vel > 200.0f
- || (y > (mDisplayHeight/2) && vel > -200.0f))) {
- // We are collapsed, and they moved enough to allow us to
- // expand. Animate in the notifications.
- mAnimAccel = 2000.0f;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are collapsed, but they didn't move sufficiently to cause
- // us to retract. Animate back to the collapsed position.
- mAnimAccel = -2000.0f;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- }
- //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
- // + " mAnimAccel=" + mAnimAccel);
-
- long now = SystemClock.uptimeMillis();
- mAnimLastTime = now;
- mCurAnimationTime = now + ANIM_FRAME_DURATION;
- mAnimating = true;
- mHandler.removeMessages(MSG_ANIMATE);
- mHandler.removeMessages(MSG_ANIMATE_REVEAL);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
- stopTracking();
- }
-
- boolean interceptTouchEvent(MotionEvent event) {
- if (SPEW) {
- Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
- + mDisabled);
- }
-
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return false;
- }
-
- final int statusBarSize = mStatusBarView.getHeight();
- final int hitSize = statusBarSize*2;
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- final int y = (int)event.getRawY();
-
- if (!mExpanded) {
- mViewDelta = statusBarSize - y;
- } else {
- mTrackingView.getLocationOnScreen(mAbsPos);
- mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
- }
- if ((!mExpanded && y < hitSize) ||
- (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
-
- // We drop events at the edge of the screen to make the windowshade come
- // down by accident less, especially when pushing open a device with a keyboard
- // that rotates (like g1 and droid)
- int x = (int)event.getRawX();
- final int edgeBorder = mEdgeBorder;
- if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
- prepareTracking(y, !mExpanded);// opening if we're not already fully visible
- mVelocityTracker.addMovement(event);
- }
- }
- } else if (mTracking) {
- mVelocityTracker.addMovement(event);
- final int minY = statusBarSize + mCloseView.getHeight();
- if (event.getAction() == MotionEvent.ACTION_MOVE) {
- int y = (int)event.getRawY();
- if (mAnimatingReveal && y < minY) {
- // nothing
- } else {
- mAnimatingReveal = false;
- updateExpandedViewPos(y + mViewDelta);
- }
- } else if (event.getAction() == MotionEvent.ACTION_UP) {
- mVelocityTracker.computeCurrentVelocity(1000);
-
- float yVel = mVelocityTracker.getYVelocity();
- boolean negative = yVel < 0;
-
- float xVel = mVelocityTracker.getXVelocity();
- if (xVel < 0) {
- xVel = -xVel;
- }
- if (xVel > 150.0f) {
- xVel = 150.0f; // limit how much we care about the x axis
- }
-
- float vel = (float)Math.hypot(yVel, xVel);
- if (negative) {
- vel = -vel;
- }
-
- performFling((int)event.getRawY(), vel, false);
- }
-
- }
- return false;
- }
-
- private class Launcher implements View.OnClickListener {
- private PendingIntent mIntent;
- private String mPkg;
- private String mTag;
- private int mId;
-
- Launcher(PendingIntent intent, String pkg, String tag, int id) {
- mIntent = intent;
- mPkg = pkg;
- mTag = tag;
- mId = id;
- }
-
- public void onClick(View v) {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManagerNative.getDefault().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- Intent overlay = new Intent();
- overlay.setSourceBounds(
- new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
- try {
- mIntent.send(mContext, 0, overlay);
- mNotificationCallbacks.onNotificationClick(mPkg, mTag, mId);
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here. Just log the exception message.
- Slog.w(TAG, "Sending contentIntent failed: " + e);
- }
- deactivate();
- }
- }
-
- private class MyTicker extends Ticker {
- MyTicker(Context context, StatusBarView sb) {
- super(context, sb);
- }
-
- @Override
- void tickerStarting() {
- mTicking = true;
- mIcons.setVisibility(View.GONE);
- mTickerView.setVisibility(View.VISIBLE);
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
- if (mExpandedVisible) {
- setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
- }
- }
-
- @Override
- void tickerDone() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
- }
- }
-
- void tickerHalting() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
- }
-
- Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
- public void onAnimationEnd(Animation animation) {
- mTicking = false;
- }
- public void onAnimationRepeat(Animation animation) {
- }
- public void onAnimationStart(Animation animation) {
- }
- };
-
- private Animation loadAnim(int id, Animation.AnimationListener listener) {
- Animation anim = AnimationUtils.loadAnimation(mContext, id);
- if (listener != null) {
- anim.setAnimationListener(listener);
- }
- return anim;
- }
-
- public String viewInfo(View v) {
- return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
- + " " + v.getWidth() + "x" + v.getHeight() + ")";
- }
-
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump StatusBar from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- synchronized (mQueueLock) {
- pw.println("Current Status Bar state:");
- pw.println(" mExpanded=" + mExpanded
- + ", mExpandedVisible=" + mExpandedVisible);
- pw.println(" mTicking=" + mTicking);
- pw.println(" mTracking=" + mTracking);
- pw.println(" mAnimating=" + mAnimating
- + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
- + ", mAnimAccel=" + mAnimAccel);
- pw.println(" mCurAnimationTime=" + mCurAnimationTime
- + " mAnimLastTime=" + mAnimLastTime);
- pw.println(" mDisplayHeight=" + mDisplayHeight
- + " mAnimatingReveal=" + mAnimatingReveal
- + " mViewDelta=" + mViewDelta);
- pw.println(" mDisplayHeight=" + mDisplayHeight);
- final int N = mQueue.size();
- pw.println(" mQueue.size=" + N);
- for (int i=0; i<N; i++) {
- PendingOp op = mQueue.get(i);
- pw.println(" [" + i + "] key=" + op.key + " code=" + op.code + " visible="
- + op.visible);
- pw.println(" iconData=" + op.iconData);
- pw.println(" notificationData=" + op.notificationData);
- }
- pw.println(" mExpandedParams: " + mExpandedParams);
- pw.println(" mExpandedView: " + viewInfo(mExpandedView));
- pw.println(" mExpandedDialog: " + mExpandedDialog);
- pw.println(" mTrackingParams: " + mTrackingParams);
- pw.println(" mTrackingView: " + viewInfo(mTrackingView));
- pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle));
- pw.println(" mOngoingItems: " + viewInfo(mOngoingItems));
- pw.println(" mLatestTitle: " + viewInfo(mLatestTitle));
- pw.println(" mLatestItems: " + viewInfo(mLatestItems));
- pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
- pw.println(" mCloseView: " + viewInfo(mCloseView));
- pw.println(" mTickerView: " + viewInfo(mTickerView));
- pw.println(" mScrollView: " + viewInfo(mScrollView)
- + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
- pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
- }
- synchronized (mIconMap) {
- final int N = mIconMap.size();
- pw.println(" mIconMap.size=" + N);
- Set<IBinder> keys = mIconMap.keySet();
- int i=0;
- for (IBinder key: keys) {
- StatusBarIcon icon = mIconMap.get(key);
- pw.println(" [" + i + "] key=" + key);
- pw.println(" data=" + icon.mData);
- i++;
- }
- }
- synchronized (mNotificationData) {
- int N = mNotificationData.ongoingCount();
- pw.println(" ongoingCount.size=" + N);
- for (int i=0; i<N; i++) {
- StatusBarNotification n = mNotificationData.getOngoing(i);
- pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
- pw.println(" data=" + n.data);
- }
- N = mNotificationData.latestCount();
- pw.println(" ongoingCount.size=" + N);
- for (int i=0; i<N; i++) {
- StatusBarNotification n = mNotificationData.getLatest(i);
- pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
- pw.println(" data=" + n.data);
- }
- }
- synchronized (mDisableRecords) {
- final int N = mDisableRecords.size();
- pw.println(" mDisableRecords.size=" + N
- + " mDisabled=0x" + Integer.toHexString(mDisabled));
- for (int i=0; i<N; i++) {
- DisableRecord tok = mDisableRecords.get(i);
- pw.println(" [" + i + "] what=0x" + Integer.toHexString(tok.what)
- + " pkg=" + tok.pkg + " token=" + tok.token);
- }
- }
-
- if (false) {
- pw.println("see the logcat for a dump of the views we have created.");
- // must happen on ui thread
- mHandler.post(new Runnable() {
- public void run() {
- mStatusBarView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mStatusBarView.getWidth() + "x"
- + mStatusBarView.getHeight());
- mStatusBarView.debug();
-
- mExpandedView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mExpandedView.getWidth() + "x"
- + mExpandedView.getHeight());
- mExpandedView.debug();
-
- mTrackingView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mTrackingView.getWidth() + "x"
- + mTrackingView.getHeight());
- mTrackingView.debug();
- }
- });
- }
- }
-
- void onBarViewAttached() {
- WindowManager.LayoutParams lp;
- int pixelFormat;
- Drawable bg;
-
- /// ---------- Tracking View --------------
- pixelFormat = PixelFormat.RGBX_8888;
- bg = mTrackingView.getBackground();
- if (bg != null) {
- pixelFormat = bg.getOpacity();
- }
-
- lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
- pixelFormat);
-// lp.token = mStatusBarView.getWindowToken();
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("TrackingView");
- lp.y = mTrackingPosition;
- mTrackingParams = lp;
-
- WindowManagerImpl.getDefault().addView(mTrackingView, lp);
- }
-
- void onTrackingViewAttached() {
- WindowManager.LayoutParams lp;
- int pixelFormat;
- Drawable bg;
-
- /// ---------- Expanded View --------------
- pixelFormat = PixelFormat.TRANSLUCENT;
-
- final int disph = mDisplay.getHeight();
- lp = mExpandedDialog.getWindow().getAttributes();
- lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
- lp.height = getExpandedHeight();
- lp.x = 0;
- mTrackingPosition = lp.y = -disph; // sufficiently large negative
- lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
- lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_DITHER
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- lp.format = pixelFormat;
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("StatusBarExpanded");
- mExpandedDialog.getWindow().setAttributes(lp);
- mExpandedDialog.getWindow().setFormat(pixelFormat);
- mExpandedParams = lp;
-
- mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
- mExpandedDialog.setContentView(mExpandedView,
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- mExpandedDialog.getWindow().setBackgroundDrawable(null);
- mExpandedDialog.show();
- FrameLayout hack = (FrameLayout)mExpandedView.getParent();
- }
-
- void setDateViewVisibility(boolean visible, int anim) {
- mDateView.setUpdates(visible);
- mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- mDateView.startAnimation(loadAnim(anim, null));
- }
-
- void setNotificationIconVisibility(boolean visible, int anim) {
- int old = mNotificationIcons.getVisibility();
- int v = visible ? View.VISIBLE : View.INVISIBLE;
- if (old != v) {
- mNotificationIcons.setVisibility(v);
- mNotificationIcons.startAnimation(loadAnim(anim, null));
- }
- }
-
- void updateExpandedViewPos(int expandedPosition) {
- if (SPEW) {
- Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
- + " mTrackingParams.y=" + mTrackingParams.y
- + " mTrackingPosition=" + mTrackingPosition);
- }
-
- int h = mStatusBarView.getHeight();
- int disph = mDisplay.getHeight();
-
- // If the expanded view is not visible, make sure they're still off screen.
- // Maybe the view was resized.
- if (!mExpandedVisible) {
- if (mTrackingView != null) {
- mTrackingPosition = -disph;
- if (mTrackingParams != null) {
- mTrackingParams.y = mTrackingPosition;
- WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
- }
- }
- if (mExpandedParams != null) {
- mExpandedParams.y = -disph;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- }
- return;
- }
-
- // tracking view...
- int pos;
- if (expandedPosition == EXPANDED_FULL_OPEN) {
- pos = h;
- }
- else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
- pos = mTrackingPosition;
- }
- else {
- if (expandedPosition <= disph) {
- pos = expandedPosition;
- } else {
- pos = disph;
- }
- pos -= disph-h;
- }
- mTrackingPosition = mTrackingParams.y = pos;
- mTrackingParams.height = disph-h;
- WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
-
- if (mExpandedParams != null) {
- mCloseView.getLocationInWindow(mPositionTmp);
- final int closePos = mPositionTmp[1];
-
- mExpandedContents.getLocationInWindow(mPositionTmp);
- final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
-
- mExpandedParams.y = pos + mTrackingView.getHeight()
- - (mTrackingParams.height-closePos) - contentsBottom;
- int max = h;
- if (mExpandedParams.y > max) {
- mExpandedParams.y = max;
- }
- int min = mTrackingPosition;
- if (mExpandedParams.y < min) {
- mExpandedParams.y = min;
- }
-
- boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
- if (!visible) {
- // if the contents aren't visible, move the expanded view way off screen
- // because the window itself extends below the content view.
- mExpandedParams.y = -disph;
- }
- panelSlightlyVisible(visible);
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- }
-
- if (SPEW) {
- Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition
- + " mTrackingParams.y=" + mTrackingParams.y
- + " mTrackingPosition=" + mTrackingPosition
- + " mExpandedParams.y=" + mExpandedParams.y
- + " mExpandedParams.height=" + mExpandedParams.height);
- }
- }
-
- int getExpandedHeight() {
- return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
- }
-
- void updateExpandedHeight() {
- if (mExpandedView != null) {
- mExpandedParams.height = getExpandedHeight();
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- }
- }
-
- /**
- * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
- * This was added last-minute and is inconsistent with the way the rest of the notifications
- * are handled, because the notification isn't really cancelled. The lights are just
- * turned off. If any other notifications happen, the lights will turn back on. Steve says
- * this is what he wants. (see bug 1131461)
- */
- private boolean mPanelSlightlyVisible;
- void panelSlightlyVisible(boolean visible) {
- if (mPanelSlightlyVisible != visible) {
- mPanelSlightlyVisible = visible;
- if (visible) {
- // tell the notification manager to turn off the lights.
- mNotificationCallbacks.onPanelRevealed();
- }
- }
- }
-
- void performDisableActions(int net) {
- int old = mDisabled;
- int diff = net ^ old;
- mDisabled = net;
-
- // act accordingly
- if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
- if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
- Slog.d(TAG, "DISABLE_EXPAND: yes");
- animateCollapse();
- }
- }
- if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
- if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
- Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
- if (mTicking) {
- mNotificationIcons.setVisibility(View.INVISIBLE);
- mTicker.halt();
- } else {
- setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
- }
- } else {
- Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
- if (!mExpandedVisible) {
- setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
- } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- mTicker.halt();
- }
- }
- }
-
- private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
- public void onClick(View v) {
- mNotificationCallbacks.onClearAll();
- addPendingOp(OP_EXPAND, null, false);
- }
- };
-
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
- || Intent.ACTION_SCREEN_OFF.equals(action)) {
- deactivate();
- }
- else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
- updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
- intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
- intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
- intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
- }
- else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
- updateResources();
- }
- }
- };
-
- void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
- if (false) {
- Slog.d(TAG, "updateNetworkName showSpn=" + showSpn + " spn=" + spn
- + " showPlmn=" + showPlmn + " plmn=" + plmn);
- }
- boolean something = false;
- if (showPlmn) {
- mPlmnLabel.setVisibility(View.VISIBLE);
- if (plmn != null) {
- mPlmnLabel.setText(plmn);
- } else {
- mPlmnLabel.setText(R.string.lockscreen_carrier_default);
- }
- } else {
- mPlmnLabel.setText("");
- mPlmnLabel.setVisibility(View.GONE);
- }
- if (showSpn && spn != null) {
- mSpnLabel.setText(spn);
- mSpnLabel.setVisibility(View.VISIBLE);
- something = true;
- } else {
- mSpnLabel.setText("");
- mSpnLabel.setVisibility(View.GONE);
- }
- }
-
- /**
- * Reload some of our resources when the configuration changes.
- *
- * We don't reload everything when the configuration changes -- we probably
- * should, but getting that smooth is tough. Someday we'll fix that. In the
- * meantime, just update the things that we know change.
- */
- void updateResources() {
- Resources res = mContext.getResources();
-
- mClearButton.setText(mContext.getText(R.string.status_bar_clear_all_button));
- mOngoingTitle.setText(mContext.getText(R.string.status_bar_ongoing_events_title));
- mLatestTitle.setText(mContext.getText(R.string.status_bar_latest_events_title));
- mNoNotificationsTitle.setText(mContext.getText(R.string.status_bar_no_notifications_title));
-
- mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
- if (false) Slog.v(TAG, "updateResources");
- }
-
- //
- // tracing
- //
-
- void postStartTracing() {
- mHandler.postDelayed(mStartTracing, 3000);
- }
-
- void vibrate() {
- android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
- Context.VIBRATOR_SERVICE);
- vib.vibrate(250);
- }
-
- Runnable mStartTracing = new Runnable() {
- public void run() {
- vibrate();
- SystemClock.sleep(250);
- Slog.d(TAG, "startTracing");
- android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
- mHandler.postDelayed(mStopTracing, 10000);
- }
- };
-
- Runnable mStopTracing = new Runnable() {
- public void run() {
- android.os.Debug.stopMethodTracing();
- Slog.d(TAG, "stopTracing");
- vibrate();
- }
- };
-
- class UninstallReceiver extends BroadcastReceiver {
- public UninstallReceiver() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
- filter.addDataScheme("package");
- mContext.registerReceiver(this, filter);
- IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(this, sdFilter);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String pkgList[] = null;
- if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- } else {
- Uri data = intent.getData();
- if (data != null) {
- String pkg = data.getSchemeSpecificPart();
- if (pkg != null) {
- pkgList = new String[]{pkg};
- }
- }
- }
- ArrayList<StatusBarNotification> list = null;
- if (pkgList != null) {
- synchronized (StatusBarService.this) {
- for (String pkg : pkgList) {
- list = mNotificationData.notificationsForPackage(pkg);
- }
- }
- }
-
- if (list != null) {
- final int N = list.size();
- for (int i=0; i<N; i++) {
- removeIcon(list.get(i).key);
- }
- }
- }
- }
-}
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index a6124bf..3602fec 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -122,6 +122,16 @@
}
},
+ new Test("Bad Icon") {
+ public void run() {
+ mNM.notify(1, new Notification(NotificationTestList.this,
+ R.layout.chrono_notification, /* not a drawable! */
+ null, System.currentTimeMillis()-(1000*60*60*24),
+ "(453) 123-2328",
+ "", null));
+ }
+ },
+
new Test("Bad resource #2") {
public void run()
{
@@ -181,7 +191,7 @@
R.drawable.icon4,
null, System.currentTimeMillis(), "Stress - Latest",
"Notify me!!!", null);
- n.flags |= Notification.FLAG_ONGOING_EVENT;
+ //n.flags |= Notification.FLAG_ONGOING_EVENT;
mNM.notify(1, n);
}
}
@@ -623,9 +633,7 @@
private PendingIntent makeIntent() {
Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setComponent(new android.content.ComponentName(
- "com.android.contacts",
- "com.android.contacts.ContactsActivity"));
+ intent.addCategory(Intent.CATEGORY_HOME);
return PendingIntent.getActivity(this, 0, intent, 0);
}
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 06506fb..3532e30 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -134,13 +134,9 @@
}, 3000);
}
},
- new Test("Expand in 3 sec.") {
+ new Test("Expand") {
public void run() {
- mHandler.postDelayed(new Runnable() {
- public void run() {
- mStatusBarManager.expand();
- }
- }, 3000);
+ mStatusBarManager.expand();
}
},
new Test("Expand in 3 sec.") {
@@ -161,14 +157,5 @@
}, 3000);
}
},
- new Test("Toggle in 3 sec.") {
- public void run() {
- mHandler.postDelayed(new Runnable() {
- public void run() {
- mStatusBarManager.toggle();
- }
- }, 3000);
- }
- },
};
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index a2f085a..755b93b 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -573,6 +573,7 @@
const String16& parentIdent,
const String16& itemIdent,
int32_t curFormat,
+ bool isFormatted,
bool pseudolocalize,
const bool overwrite,
ResourceTable* outTable)
@@ -583,7 +584,7 @@
String16 str;
Vector<StringPool::entry_style_span> spans;
err = parseStyledString(bundle, in->getPrintableSource().string(),
- block, item16, &str, &spans,
+ block, item16, &str, &spans, isFormatted,
pseudolocalize);
if (err != NO_ERROR) {
return err;
@@ -616,6 +617,7 @@
const String16& curTag,
bool curIsStyled,
int32_t curFormat,
+ bool isFormatted,
bool pseudolocalize,
const bool overwrite,
ResourceTable* outTable)
@@ -626,7 +628,7 @@
Vector<StringPool::entry_style_span> spans;
err = parseStyledString(bundle, in->getPrintableSource().string(), block,
curTag, &str, curIsStyled ? &spans : NULL,
- pseudolocalize);
+ isFormatted, pseudolocalize);
if (err < NO_ERROR) {
return err;
@@ -709,12 +711,18 @@
// useful attribute names and special values
const String16 name16("name");
const String16 translatable16("translatable");
+ const String16 formatted16("formatted");
const String16 false16("false");
const String16 myPackage(assets->getPackage());
bool hasErrors = false;
-
+
+ bool fileIsTranslatable = true;
+ if (strstr(in->getPrintableSource().string(), "donottranslate") != NULL) {
+ fileIsTranslatable = false;
+ }
+
DefaultKeyedVector<String16, uint32_t> nextPublicId(0);
ResXMLTree::event_code_t code;
@@ -751,6 +759,7 @@
bool curIsBagReplaceOnOverwrite = false;
bool curIsStyled = false;
bool curIsPseudolocalizable = false;
+ bool curIsFormatted = fileIsTranslatable;
bool localHasErrors = false;
if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
@@ -1136,6 +1145,7 @@
String8 locale(rawLocale);
String16 name;
String16 translatable;
+ String16 formatted;
size_t n = block.getAttributeCount();
for (size_t i = 0; i < n; i++) {
@@ -1145,11 +1155,14 @@
name.setTo(block.getAttributeStringValue(i, &length));
} else if (strcmp16(attr, translatable16.string()) == 0) {
translatable.setTo(block.getAttributeStringValue(i, &length));
+ } else if (strcmp16(attr, formatted16.string()) == 0) {
+ formatted.setTo(block.getAttributeStringValue(i, &length));
}
}
if (name.size() > 0) {
if (translatable == false16) {
+ 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"
@@ -1167,6 +1180,10 @@
} else {
outTable->addLocalization(name, locale);
}
+
+ if (formatted == false16) {
+ curIsFormatted = false;
+ }
}
curTag = &string16;
@@ -1356,7 +1373,7 @@
block.getPosition(&parserPosition);
err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
- ident, parentIdent, itemIdent, curFormat,
+ ident, parentIdent, itemIdent, curFormat, curIsFormatted,
false, overwrite, outTable);
if (err == NO_ERROR) {
if (curIsPseudolocalizable && localeIsDefined(curParams)
@@ -1365,8 +1382,8 @@
#if 1
block.setPosition(parserPosition);
err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
- curType, ident, parentIdent, itemIdent, curFormat, true,
- overwrite, outTable);
+ curType, ident, parentIdent, itemIdent, curFormat,
+ curIsFormatted, true, overwrite, outTable);
#endif
}
}
@@ -1389,7 +1406,8 @@
block.getPosition(&parserPosition);
err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
- *curTag, curIsStyled, curFormat, false, overwrite, outTable);
+ *curTag, curIsStyled, curFormat, curIsFormatted,
+ false, overwrite, outTable);
if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
hasErrors = localHasErrors = true;
@@ -1400,7 +1418,8 @@
// pseudolocalize here
block.setPosition(parserPosition);
err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
- ident, *curTag, curIsStyled, curFormat, true, overwrite, outTable);
+ ident, *curTag, curIsStyled, curFormat,
+ curIsFormatted, true, overwrite, outTable);
if (err != NO_ERROR) {
hasErrors = localHasErrors = true;
}
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 4c59288..57ff47a 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -68,12 +68,118 @@
return String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize);
}
+status_t hasSubstitutionErrors(const char* fileName,
+ ResXMLTree* inXml,
+ String16 str16)
+{
+ const char16_t* str = str16.string();
+ const char16_t* p = str;
+ const char16_t* end = str + str16.size();
+
+ bool nonpositional = false;
+ int argCount = 0;
+
+ while (p < end) {
+ /*
+ * Look for the start of a Java-style substitution sequence.
+ */
+ if (*p == '%' && p + 1 < end) {
+ p++;
+
+ // A literal percent sign represented by %%
+ if (*p == '%') {
+ p++;
+ continue;
+ }
+
+ argCount++;
+
+ if (*p >= '0' && *p <= '9') {
+ do {
+ p++;
+ } while (*p >= '0' && *p <= '9');
+ if (*p != '$') {
+ // This must be a size specification instead of position.
+ nonpositional = true;
+ }
+ } else if (*p == '<') {
+ // Reusing last argument; bad idea since it can be re-arranged.
+ nonpositional = true;
+ p++;
+
+ // Optionally '$' can be specified at the end.
+ if (p < end && *p == '$') {
+ p++;
+ }
+ } else {
+ nonpositional = true;
+ }
+
+ // Ignore flags and widths
+ while (p < end && (*p == '-' ||
+ *p == '#' ||
+ *p == '+' ||
+ *p == ' ' ||
+ *p == ',' ||
+ *p == '(' ||
+ (*p >= '0' && *p <= '9'))) {
+ p++;
+ }
+
+ /*
+ * This is a shortcut to detect strings that are going to Time.format()
+ * instead of String.format()
+ *
+ * Comparison of String.format() and Time.format() args:
+ *
+ * String: ABC E GH ST X abcdefgh nost x
+ * Time: DEFGHKMS W Za d hkm s w yz
+ *
+ * Therefore we know it's definitely Time if we have:
+ * DFKMWZkmwyz
+ */
+ if (p < end) {
+ switch (*p) {
+ case 'D':
+ case 'F':
+ case 'K':
+ case 'M':
+ case 'W':
+ case 'Z':
+ case 'k':
+ case 'm':
+ case 'w':
+ case 'y':
+ case 'z':
+ return NO_ERROR;
+ }
+ }
+ }
+
+ p++;
+ }
+
+ /*
+ * If we have more than one substitution in this string and any of them
+ * are not in positional form, give the user an error.
+ */
+ if (argCount > 1 && nonpositional) {
+ SourcePos(String8(fileName), inXml->getLineNumber()).error(
+ "Multiple substitutions specified in non-positional format; "
+ "did you mean to add the formatted=\"true\" attribute?\n");
+ return NOT_ENOUGH_DATA;
+ }
+
+ return NO_ERROR;
+}
+
status_t parseStyledString(Bundle* bundle,
const char* fileName,
ResXMLTree* inXml,
const String16& endTag,
String16* outString,
Vector<StringPool::entry_style_span>* outSpans,
+ bool isFormatted,
bool pseudolocalize)
{
Vector<StringPool::entry_style_span> spanStack;
@@ -101,7 +207,11 @@
std::string pseudo = pseudolocalize_string(orig);
curString.append(String16(String8(pseudo.c_str())));
} else {
- curString.append(text);
+ if (isFormatted && hasSubstitutionErrors(fileName, inXml, text) != NO_ERROR) {
+ return UNKNOWN_ERROR;
+ } else {
+ curString.append(text);
+ }
}
} else if (code == ResXMLTree::START_TAG) {
const String16 element16(inXml->getElementName(&len));
diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h
index e9a263b..05624b7 100644
--- a/tools/aapt/XMLNode.h
+++ b/tools/aapt/XMLNode.h
@@ -25,6 +25,7 @@
const String16& endTag,
String16* outString,
Vector<StringPool::entry_style_span>* outSpans,
+ bool isFormatted,
bool isPseudolocalizable);
void printXMLBlock(ResXMLTree* block);