Merge "Add a new state machine for handling the incoming / outgoing profile connections." into kraken
diff --git a/Android.mk b/Android.mk
index 5c95d20..3041065 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 \
@@ -403,6 +404,8 @@
resources/samples/CubeLiveWallpaper "Live Wallpaper" \
-samplecode $(sample_dir)/Home \
resources/samples/Home "Home" \
+ -samplecode $(sample_dir)/HeavyWeight \
+ resources/samples/HeavyWeight "Heavy Weight App" \
-samplecode $(sample_dir)/JetBoy \
resources/samples/JetBoy "JetBoy" \
-samplecode $(sample_dir)/LunarLander \
@@ -411,8 +414,8 @@
resources/samples/MultiResolution "Multiple Resolutions" \
-samplecode $(sample_dir)/NotePad \
resources/samples/NotePad "Note Pad" \
- -samplecode $(sample_dir)/SampleSyncAdapter \
- resources/samples/SampleSyncAdapter "Sample Sync Adapter" \
+ -samplecode $(sample_dir)/SampleSyncAdapter \
+ resources/samples/SampleSyncAdapter "Sample Sync Adapter" \
-samplecode $(sample_dir)/SearchableDictionary \
resources/samples/SearchableDictionary "Searchable Dictionary v2" \
-samplecode $(sample_dir)/Snake \
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/api/current.xml b/api/current.xml
index e6780be..d002534 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -4508,6 +4508,17 @@
visibility="public"
>
</field>
+<field name="heavyWeight"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843456"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="height"
type="int"
transient="false"
@@ -5916,17 +5927,6 @@
visibility="public"
>
</field>
-<field name="kraken_resource_pad65"
- type="int"
- transient="false"
- volatile="false"
- value="16843456"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="kraken_resource_pad7"
type="int"
transient="false"
@@ -21759,6 +21759,17 @@
visibility="public"
>
</field>
+<field name="IMPORTANCE_HEAVY_WEIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="150"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="IMPORTANCE_SERVICE"
type="int"
transient="false"
@@ -26393,6 +26404,17 @@
<parameter name="parcel" type="android.os.Parcel">
</parameter>
</constructor>
+<method name="clone"
+ return="android.app.Notification"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="describeContents"
return="int"
abstract="false"
@@ -44092,6 +44114,17 @@
visibility="public"
>
</method>
+<method name="getTargetPackage"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="readIntentSenderOrNullFromParcel"
return="android.content.IntentSender"
abstract="false"
@@ -46286,6 +46319,17 @@
visibility="public"
>
</field>
+<field name="FLAG_HEAVY_WEIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1048576"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_KILL_AFTER_RESTORE"
type="int"
transient="false"
@@ -138860,6 +138904,17 @@
visibility="public"
>
</field>
+<field name="ACTION_APPLICATION_DETAILS_SETTINGS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.settings.APPLICATION_DETAILS_SETTINGS""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_APPLICATION_DEVELOPMENT_SETTINGS"
type="java.lang.String"
transient="false"
@@ -211346,6 +211401,17 @@
<parameter name="parent" type="android.view.ViewGroup">
</parameter>
</method>
+<method name="clone"
+ return="android.widget.RemoteViews"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="describeContents"
return="int"
abstract="false"
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c9096cf..793b9d2 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -726,6 +726,12 @@
public static final int IMPORTANCE_FOREGROUND = 100;
/**
+ * Constant for {@link #importance}: this process is running a
+ * heavy-weight application and thus should not be killed.
+ */
+ public static final int IMPORTANCE_HEAVY_WEIGHT = 150;
+
+ /**
* Constant for {@link #importance}: this process is running something
* that is considered to be actively visible to the user.
*/
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f694285..b78d22f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1251,6 +1251,13 @@
reply.writeNoException();
return true;
}
+
+ case FINISH_HEAVY_WEIGHT_APP_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ finishHeavyWeightApp();
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -2758,5 +2765,15 @@
return res;
}
+ public void finishHeavyWeightApp() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(FINISH_HEAVY_WEIGHT_APP_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 31f0a63..cd24fa6 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -303,6 +303,8 @@
public boolean isUserAMonkey() throws RemoteException;
+ public void finishHeavyWeightApp() throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -513,4 +515,5 @@
int WILL_ACTIVITY_BE_VISIBLE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+105;
int START_ACTIVITY_WITH_CONFIG_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+106;
int GET_RUNNING_EXTERNAL_APPLICATIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+107;
+ int FINISH_HEAVY_WEIGHT_APP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+108;
}
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/content/IntentSender.java b/core/java/android/content/IntentSender.java
index e182021..007a715 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -16,6 +16,7 @@
package android.content;
+import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.Intent;
import android.content.IIntentSender;
@@ -170,6 +171,25 @@
}
/**
+ * Return the package name of the application that created this
+ * IntentSender, that is the identity under which you will actually be
+ * sending the Intent. The returned string is supplied by the system, so
+ * that an application can not spoof its package.
+ *
+ * @return The package name of the PendingIntent, or null if there is
+ * none associated with it.
+ */
+ public String getTargetPackage() {
+ try {
+ return ActivityManagerNative.getDefault()
+ .getPackageForIntentSender(mTarget);
+ } catch (RemoteException e) {
+ // Should never happen.
+ return null;
+ }
+ }
+
+ /**
* Comparison operator on two IntentSender objects, such that true
* is returned then they both represent the same operation from the
* same package.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 7047113..7901b155 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -273,6 +273,17 @@
public static final int FLAG_SUPPORTS_XLARGE_SCREENS = 1<<19;
/**
+ * Value for {@link #flags}: set to <code>true</code> if the application
+ * has reported that it is heavy-weight, and thus can not participate in
+ * the normal application lifecycle.
+ *
+ * <p>Comes from the
+ * {@link android.R.styleable#AndroidManifestApplication_heavyWeight android:heavyWeight}
+ * attribute of the <application> tag.
+ */
+ public static final int FLAG_HEAVY_WEIGHT = 1<<20;
+
+ /**
* Value for {@link #flags}: this is true if the application has set
* its android:neverEncrypt to true, false otherwise. It is used to specify
* that this package specifically "opts-out" of a secured file system solution,
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4ddc124..a5f5acc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1600,6 +1600,18 @@
ai.enabled = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_enabled, true);
+
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_heavyWeight,
+ false)) {
+ ai.flags |= ApplicationInfo.FLAG_HEAVY_WEIGHT;
+
+ // A heavy-weight application can not be in a custom process.
+ // We can do direct compare because we intern all strings.
+ if (ai.processName != null && ai.processName != ai.packageName) {
+ outError[0] = "Heavy-weight applications can not use custom processes";
+ }
+ }
}
sa.recycle();
@@ -1889,6 +1901,14 @@
sa.recycle();
+ if (receiver && (owner.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+ // A heavy-weight application can not have receives in its main process
+ // We can do direct compare because we intern all strings.
+ if (a.info.processName == owner.packageName) {
+ outError[0] = "Heavy-weight applications can not have receivers in main process";
+ }
+ }
+
if (outError[0] != null) {
return null;
}
@@ -2171,6 +2191,15 @@
sa.recycle();
+ if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+ // A heavy-weight application can not have providers in its main process
+ // We can do direct compare because we intern all strings.
+ if (p.info.processName == owner.packageName) {
+ outError[0] = "Heavy-weight applications can not have providers in main process";
+ return null;
+ }
+ }
+
if (cpname == null) {
outError[0] = "<provider> does not incude authorities attribute";
return null;
@@ -2403,6 +2432,15 @@
sa.recycle();
+ if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+ // A heavy-weight application can not have services in its main process
+ // We can do direct compare because we intern all strings.
+ if (s.info.processName == owner.packageName) {
+ outError[0] = "Heavy-weight applications can not have services in main process";
+ return null;
+ }
+ }
+
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
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/provider/Settings.java b/core/java/android/provider/Settings.java
index 9f19f11..7bb89f5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -355,6 +355,21 @@
"android.settings.MANAGE_APPLICATIONS_SETTINGS";
/**
+ * Activity Action: Show screen of details about a particular application.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: The Intent's data URI specifies the application package name
+ * to be shown, with the "package" scheme. That is "package:com.my.app".
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_APPLICATION_DETAILS_SETTINGS =
+ "android.settings.APPLICATION_DETAILS_SETTINGS";
+
+ /**
* Activity Action: Show settings for system update functionality.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -2368,6 +2383,24 @@
public static final String TETHER_SUPPORTED = "tether_supported";
/**
+ * Used to require DUN APN on the device or not - defaults to a build config value
+ * which defaults to false
+ * @hide
+ */
+ public static final String TETHER_DUN_REQUIRED = "tether_dun_required";
+
+ /**
+ * Used to hold a gservices-provisioned apn value for DUN. If set, or the
+ * corresponding build config values are set it will override the APN DB
+ * values.
+ * Consists of a comma seperated list of strings:
+ * "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
+ * note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN"
+ * @hide
+ */
+ public static final String TETHER_DUN_APN = "tether_dun_apn";
+
+ /**
* No longer supported.
*/
public static final String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
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/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 921d0f5..ac23832 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1380,16 +1380,23 @@
final File temp = new File(dest.getPath() + ".writing");
new Thread(new Runnable() {
public void run() {
+ FileOutputStream out = null;
try {
- FileOutputStream out = new FileOutputStream(temp);
+ out = new FileOutputStream(temp);
p.writeToStream(out);
- out.close();
// Writing the picture succeeded, rename the temporary file
// to the destination.
temp.renameTo(dest);
} catch (Exception e) {
// too late to do anything about it.
} finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (Exception e) {
+ // Can't do anything about that
+ }
+ }
temp.delete();
}
}
@@ -1442,20 +1449,23 @@
final Bundle copy = new Bundle(b);
new Thread(new Runnable() {
public void run() {
- final Picture p = Picture.createFromStream(in);
- if (p != null) {
- // Post a runnable on the main thread to update the
- // history picture fields.
- mPrivateHandler.post(new Runnable() {
- public void run() {
- restoreHistoryPictureFields(p, copy);
- }
- });
- }
try {
- in.close();
- } catch (Exception e) {
- // Nothing we can do now.
+ final Picture p = Picture.createFromStream(in);
+ if (p != null) {
+ // Post a runnable on the main thread to update the
+ // history picture fields.
+ mPrivateHandler.post(new Runnable() {
+ public void run() {
+ restoreHistoryPictureFields(p, copy);
+ }
+ });
+ }
+ } finally {
+ try {
+ in.close();
+ } catch (Exception e) {
+ // Nothing we can do now.
+ }
}
}
}).start();
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/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
new file mode 100644
index 0000000..ada7f36
--- /dev/null
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -0,0 +1,156 @@
+/*
+ * 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.app;
+
+import com.android.internal.R;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * This activity is displayed when the system attempts to start an Intent for
+ * which there is more than one matching activity, allowing the user to decide
+ * which to go to. It is not normally used directly by application developers.
+ */
+public class HeavyWeightSwitcherActivity extends Activity {
+ /** The PendingIntent of the new activity being launched. */
+ public static final String KEY_INTENT = "intent";
+ /** Set if the caller is requesting a result. */
+ public static final String KEY_HAS_RESULT = "has_result";
+ /** Package of current heavy-weight app. */
+ public static final String KEY_CUR_APP = "cur_app";
+ /** Task that current heavy-weight activity is running in. */
+ public static final String KEY_CUR_TASK = "cur_task";
+ /** Package of newly requested heavy-weight app. */
+ public static final String KEY_NEW_APP = "new_app";
+
+ IntentSender mStartIntent;
+ boolean mHasResult;
+ String mCurApp;
+ int mCurTask;
+ String mNewApp;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestWindowFeature(Window.FEATURE_LEFT_ICON);
+
+ mStartIntent = (IntentSender)getIntent().getParcelableExtra(KEY_INTENT);
+ mHasResult = getIntent().getBooleanExtra(KEY_HAS_RESULT, false);
+ mCurApp = getIntent().getStringExtra(KEY_CUR_APP);
+ mCurTask = getIntent().getIntExtra(KEY_CUR_TASK, 0);
+ mNewApp = getIntent().getStringExtra(KEY_NEW_APP);
+
+ setContentView(com.android.internal.R.layout.heavy_weight_switcher);
+
+ setIconAndText(R.id.old_app_icon, R.id.old_app_action, R.id.old_app_description,
+ mCurApp, R.string.old_app_action, R.string.old_app_description);
+ setIconAndText(R.id.new_app_icon, R.id.new_app_action, R.id.new_app_description,
+ mNewApp, R.string.new_app_action, R.string.new_app_description);
+
+ View button = findViewById((R.id.switch_old));
+ button.setOnClickListener(mSwitchOldListener);
+ button = findViewById((R.id.switch_new));
+ button.setOnClickListener(mSwitchNewListener);
+ button = findViewById((R.id.cancel));
+ button.setOnClickListener(mCancelListener);
+
+ getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
+ android.R.drawable.ic_dialog_alert);
+ }
+
+ void setText(int id, CharSequence text) {
+ ((TextView)findViewById(id)).setText(text);
+ }
+
+ void setDrawable(int id, Drawable dr) {
+ if (dr != null) {
+ ((ImageView)findViewById(id)).setImageDrawable(dr);
+ }
+ }
+
+ void setIconAndText(int iconId, int actionId, int descriptionId,
+ String packageName, int actionStr, int descriptionStr) {
+ CharSequence appName = "";
+ Drawable appIcon = null;
+ if (mCurApp != null) {
+ try {
+ ApplicationInfo info = getPackageManager().getApplicationInfo(
+ packageName, 0);
+ appName = info.loadLabel(getPackageManager());
+ appIcon = info.loadIcon(getPackageManager());
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ setDrawable(iconId, appIcon);
+ setText(actionId, getString(actionStr, appName));
+ setText(descriptionId, getText(descriptionStr));
+ }
+
+ private OnClickListener mSwitchOldListener = new OnClickListener() {
+ public void onClick(View v) {
+ try {
+ ActivityManagerNative.getDefault().moveTaskToFront(mCurTask);
+ } catch (RemoteException e) {
+ }
+ finish();
+ }
+ };
+
+ private OnClickListener mSwitchNewListener = new OnClickListener() {
+ public void onClick(View v) {
+ try {
+ ActivityManagerNative.getDefault().finishHeavyWeightApp();
+ } catch (RemoteException e) {
+ }
+ try {
+ if (mHasResult) {
+ startIntentSenderForResult(mStartIntent, -1, null,
+ Intent.FLAG_ACTIVITY_FORWARD_RESULT,
+ Intent.FLAG_ACTIVITY_FORWARD_RESULT, 0);
+ } else {
+ startIntentSenderForResult(mStartIntent, -1, null, 0, 0, 0);
+ }
+ } catch (IntentSender.SendIntentException ex) {
+ Log.w("HeavyWeightSwitcherActivity", "Failure starting", ex);
+ }
+ finish();
+ }
+ };
+
+ private OnClickListener mCancelListener = new OnClickListener() {
+ public void onClick(View v) {
+ finish();
+ }
+ };
+}
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/jni/Android.mk b/core/jni/Android.mk
index dbad7e9..d4545d7 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -122,6 +122,7 @@
android_server_BluetoothService.cpp \
android_server_BluetoothEventLoop.cpp \
android_server_BluetoothA2dpService.cpp \
+ android_server_Watchdog.cpp \
android_message_digest_sha1.cpp \
android_ddm_DdmHandleNativeHeap.cpp \
com_android_internal_os_ZygoteInit.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 76df9db..f66ed83 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -154,6 +154,7 @@
extern int register_android_server_BluetoothService(JNIEnv* env);
extern int register_android_server_BluetoothEventLoop(JNIEnv *env);
extern int register_android_server_BluetoothA2dpService(JNIEnv* env);
+extern int register_android_server_Watchdog(JNIEnv* env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env);
extern int register_android_backup_BackupDataInput(JNIEnv *env);
@@ -1278,6 +1279,7 @@
REG_JNI(register_android_server_BluetoothService),
REG_JNI(register_android_server_BluetoothEventLoop),
REG_JNI(register_android_server_BluetoothA2dpService),
+ REG_JNI(register_android_server_Watchdog),
REG_JNI(register_android_message_digest_sha1),
REG_JNI(register_android_ddm_DdmHandleNativeHeap),
REG_JNI(register_android_backup_BackupDataInput),
diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp
index cf53a06..8c795af 100644
--- a/core/jni/android_server_BluetoothA2dpService.cpp
+++ b/core/jni/android_server_BluetoothA2dpService.cpp
@@ -256,10 +256,12 @@
parse_property_change(env, msg, (Properties *)&sink_properties,
sizeof(sink_properties) / sizeof(Properties));
const char *c_path = dbus_message_get_path(msg);
+ jstring path = env->NewStringUTF(c_path);
env->CallVoidMethod(nat->me,
method_onSinkPropertyChanged,
- env->NewStringUTF(c_path),
+ path,
str_array);
+ env->DeleteLocalRef(path);
result = DBUS_HANDLER_RESULT_HANDLED;
return result;
} else {
@@ -292,10 +294,13 @@
result = JNI_FALSE;
}
LOGV("... Device Path = %s, result = %d", path, result);
+
+ jstring jPath = env->NewStringUTF(path);
env->CallVoidMethod(nat->me,
method_onConnectSinkResult,
- env->NewStringUTF(path),
+ jPath,
result);
+ env->DeleteLocalRef(jPath);
free(user);
}
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index 259cc01..01b6711 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -1061,6 +1061,8 @@
DBusError err;
dbus_error_init(&err);
JNIEnv *env;
+ jstring addr;
+
nat->vm->GetEnv((void**)&env, nat->envVer);
LOGV("... address = %s", address);
@@ -1109,10 +1111,12 @@
}
}
+ addr = env->NewStringUTF(address);
env->CallVoidMethod(nat->me,
method_onCreatePairedDeviceResult,
- env->NewStringUTF(address),
+ addr,
result);
+ env->DeleteLocalRef(addr);
done:
dbus_error_free(&err);
free(user);
@@ -1139,10 +1143,12 @@
}
LOG_AND_FREE_DBUS_ERROR(&err);
}
+ jstring addr = env->NewStringUTF(address);
env->CallVoidMethod(nat->me,
method_onCreateDeviceResult,
- env->NewStringUTF(address),
+ addr,
result);
+ env->DeleteLocalRef(addr);
free(user);
}
@@ -1163,10 +1169,12 @@
LOG_AND_FREE_DBUS_ERROR(&err);
result = JNI_FALSE;
}
+ jstring jPath = env->NewStringUTF(path);
env->CallVoidMethod(nat->me,
method_onDiscoverServicesResult,
- env->NewStringUTF(path),
+ jPath,
result);
+ env->DeleteLocalRef(jPath);
free(user);
}
@@ -1194,10 +1202,12 @@
}
done:
+ jstring addr = env->NewStringUTF(address);
env->CallVoidMethod(nat->me,
method_onGetDeviceServiceChannelResult,
- env->NewStringUTF(address),
+ addr,
channel);
+ env->DeleteLocalRef(addr);
free(user);
}
#endif
diff --git a/core/jni/android_server_Watchdog.cpp b/core/jni/android_server_Watchdog.cpp
new file mode 100644
index 0000000..2a90db7
--- /dev/null
+++ b/core/jni/android_server_Watchdog.cpp
@@ -0,0 +1,111 @@
+/*
+ ** Copyright 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.
+ */
+
+#define LOG_TAG "Watchdog_N"
+#include <utils/Log.h>
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include <android_runtime/AndroidRuntime.h>
+
+static void dumpOneStack(int tid, int outFd) {
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "/proc/%d/stack", tid);
+ int stackFd = open(buf, O_RDONLY);
+ if (stackFd >= 0) {
+ // header for readability
+ strncat(buf, ":\n", sizeof(buf) - strlen(buf) - 1);
+ write(outFd, buf, strlen(buf));
+
+ // copy the stack dump text
+ int nBytes;
+ while ((nBytes = read(stackFd, buf, sizeof(buf))) > 0) {
+ write(outFd, buf, nBytes);
+ }
+
+ // footer and done
+ write(outFd, "\n", 1);
+ close(stackFd);
+ } else {
+ LOGE("Unable to open stack of tid %d : %d (%s)", tid, errno, strerror(errno));
+ }
+}
+
+static void dumpKernelStacks(JNIEnv* env, jobject clazz, jstring pathStr) {
+ char buf[128];
+ DIR* taskdir;
+
+ LOGI("dumpKernelStacks");
+ if (!pathStr) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Null path");
+ return;
+ }
+
+ const char *path = env->GetStringUTFChars(pathStr, NULL);
+
+ int outFd = open(path, O_WRONLY | O_APPEND | O_CREAT);
+ if (outFd < 0) {
+ LOGE("Unable to open stack dump file: %d (%s)", errno, strerror(errno));
+ goto done;
+ }
+
+ snprintf(buf, sizeof(buf), "\n----- begin pid %d kernel stacks -----\n", getpid());
+ write(outFd, buf, strlen(buf));
+
+ // look up the list of all threads in this process
+ snprintf(buf, sizeof(buf), "/proc/%d/task", getpid());
+ taskdir = opendir(buf);
+ if (taskdir != NULL) {
+ struct dirent * ent;
+ while ((ent = readdir(taskdir)) != NULL) {
+ int tid = atoi(ent->d_name);
+ if (tid > 0 && tid <= 65535) {
+ // dump each stack trace
+ dumpOneStack(tid, outFd);
+ }
+ }
+ closedir(taskdir);
+ }
+
+ snprintf(buf, sizeof(buf), "----- end pid %d kernel stacks -----\n", getpid());
+ write(outFd, buf, strlen(buf));
+
+ close(outFd);
+done:
+ env->ReleaseStringUTFChars(pathStr, path);
+}
+
+// ----------------------------------------
+
+namespace android {
+
+static const JNINativeMethod g_methods[] = {
+ { "native_dumpKernelStacks", "(Ljava/lang/String;)V", (void*)dumpKernelStacks },
+};
+
+int register_android_server_Watchdog(JNIEnv* env) {
+ return AndroidRuntime::registerNativeMethods(env, "com/android/server/Watchdog",
+ g_methods, NELEM(g_methods));
+}
+
+}
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 838ef22..06fa84b 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "Surface"
+
#include <stdio.h>
#include "android_util_Binder.h"
@@ -226,8 +228,9 @@
doThrow(env, "java/lang/NullPointerException", NULL);
return;
}
- sp<Surface> rhs = new Surface(*parcel);
- setSurface(env, clazz, rhs);
+
+ sp<Surface> sur(Surface::readFromParcel(*parcel, 0));
+ setSurface(env, clazz, sur);
}
static jint Surface_getIdentity(JNIEnv* env, jobject clazz)
@@ -586,7 +589,7 @@
* a Surface and is necessary for returning the Surface reference to
* the caller. At this point, we should only have a SurfaceControl.
*/
-
+
const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz);
const sp<SurfaceControl>& rhs = getSurfaceControl(env, other);
if (!SurfaceControl::isSameSurface(surface, rhs)) {
@@ -605,13 +608,9 @@
return;
}
- const sp<Surface>& control(getSurface(env, clazz));
- sp<Surface> rhs = new Surface(*parcel);
- if (!Surface::isSameSurface(control, rhs)) {
- // we reassign the surface only if it's a different one
- // otherwise we would loose our client-side state.
- setSurface(env, clazz, rhs);
- }
+ const sp<Surface>& curr(getSurface(env, clazz));
+ sp<Surface> sur(Surface::readFromParcel(*parcel, curr));
+ setSurface(env, clazz, sur);
}
static void Surface_writeToParcel(
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2a2208f..1fcf186 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1250,6 +1250,12 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+ <activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity"
+ android:theme="@style/Theme.Dialog"
+ android:label="@string/heavy_weight_switcher_title"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
<activity android:name="com.android.internal.app.DisableCarModeActivity"
android:theme="@style/Theme.NoDisplay"
android:excludeFromRecents="true">
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/heavy_weight_switcher.xml b/core/res/res/layout/heavy_weight_switcher.xml
new file mode 100644
index 0000000..9acf009
--- /dev/null
+++ b/core/res/res/layout/heavy_weight_switcher.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:padding="4dp"
+ android:gravity="center_horizontal"
+ android:layout_width="wrap_content" android:layout_height="wrap_content">
+
+ <TextView
+ android:layout_width="match_parent" android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:paddingBottom="8dp"
+ android:text="@string/heavy_weight_switcher_text"/>
+
+ <ImageView android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"
+ android:src="?android:listDivider" />
+
+ <LinearLayout android:id="@+id/switch_old"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:background="@android:drawable/list_selector_background"
+ android:paddingRight="3dip"
+ android:paddingLeft="3dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="14dip"
+ android:gravity="center_vertical"
+ android:focusable="true" >
+
+ <ImageView android:id="@+id/old_app_icon"
+ android:layout_width="@android:dimen/app_icon_size"
+ android:layout_height="@android:dimen/app_icon_size"
+ android:layout_marginRight="11dip"
+ android:layout_gravity="center_vertical"
+ android:scaleType="fitCenter"/>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:duplicateParentState="true" >
+ <TextView android:id="@+id/old_app_action"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:layout_marginBottom="2dip"
+ android:duplicateParentState="true" />
+ <TextView android:id="@+id/old_app_description"
+ android:layout_marginTop="-4dip"
+ android:layout_gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:duplicateParentState="true" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"
+ android:src="?android:listDivider" />
+
+ <LinearLayout android:id="@+id/switch_new"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:background="@android:drawable/list_selector_background"
+ android:paddingRight="3dip"
+ android:paddingLeft="3dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="8dip"
+ android:gravity="center_vertical"
+ android:focusable="true" >
+
+ <ImageView android:id="@+id/new_app_icon"
+ android:layout_width="@android:dimen/app_icon_size"
+ android:layout_height="@android:dimen/app_icon_size"
+ android:layout_marginRight="11dip"
+ android:layout_gravity="center_vertical"
+ android:scaleType="fitCenter"/>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:duplicateParentState="true" >
+ <TextView android:id="@+id/new_app_action"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:layout_marginBottom="2dip"
+ android:duplicateParentState="true" />
+ <TextView android:id="@+id/new_app_description"
+ android:layout_marginTop="-4dip"
+ android:layout_gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:duplicateParentState="true" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"
+ android:src="?android:listDivider" />
+
+ <TextView android:id="@+id/cancel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:background="@android:drawable/list_selector_background"
+ android:paddingRight="6dip"
+ android:paddingLeft="6dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="8dip"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:gravity="center"
+ android:focusable="true"
+ android:text="@string/cancel" />
+
+</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..04937565 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/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b4c4811..5ca7b28 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -740,6 +740,15 @@
<attr name="restoreNeedsApplication" />
<attr name="restoreAnyVersion" />
<attr name="neverEncrypt" />
+ <!-- Declare that this is a heavy-weight application. This kind of
+ application is not able to save and restore its state on demand,
+ so can not participate in the normal activity lifecycle. It will
+ not be killed while in the background; the user must explicitly
+ quit it. Only one such app can be running at a time; if the user
+ tries to launch a second heavy-weight app, they will be prompted
+ to quit the first before doing so. While a heavy-weight
+ application is running, the user will be informed of this. -->
+ <attr name="heavyWeight" format="boolean" />
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 64f05fe..1946388 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -91,8 +91,18 @@
<string-array translatable="false" name="config_tether_upstream_regexs">
</string-array>
- <!-- Boolean indicating if we require the use of DUN on mobile for tethering -->
- <bool translatable="false" name="config_tether_dun_required">true</bool>
+ <!-- Boolean indicating if we require the use of DUN on mobile for tethering.
+ Note that this defaults to false so that if you move to a carrier that
+ hasn't configured anything tethering will still work. If you'd rather
+ make the device untetherable on unconfigured devices, set to true -->
+ <bool translatable="false" name="config_tether_dun_required">false</bool>
+
+ <!-- String containing the apn value for tethering. May be overriden by secure settings
+ TETHER_DUN_APN. Value is a comma separated series of strings:
+ "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
+ note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
+ <string translatable="false" name="config_tether_apndata"></string>
+
<!-- Flag indicating whether the keyguard should be bypassed when
the slider is open. This can be set or unset depending how easily
@@ -220,9 +230,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/public.xml b/core/res/res/values/public.xml
index 5d18e9e..1932771 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1245,8 +1245,9 @@
<public type="attr" name="logo" id="0x010102be" />
<public type="attr" name="xlargeScreens" id="0x010102bf" />
-
+ <public type="attr" name="heavyWeight" id="0x010102c0" />
<public-padding type="attr" name="kraken_resource_pad" end="0x01010300" />
+
<public-padding type="id" name="kraken_resource_pad" end="0x01020040" />
<public-padding type="anim" name="kraken_resource_pad" end="0x010a0020" />
<public-padding type="drawable" name="kraken_resource_pad" end="0x01080100" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1e007d36..e7f892a 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. -->
@@ -1974,8 +1935,29 @@
<!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. -->
<string name="wait">Wait</string>
+ <!-- Notification text to tell the user that a heavy-weight application is running. -->
+ <string name="heavy_weight_notification"><xliff:g id="app">%1$s</xliff:g> running</string>
+
+ <!-- Notification details to tell the user that a heavy-weight application is running. -->
+ <string name="heavy_weight_notification_detail">Select to switch to application</string>
+
+ <!-- Title of dialog prompting whether user wants to switch between heavy-weight apps. -->
+ <string name="heavy_weight_switcher_title">Switch applications?</string>
+
+ <!-- Descriptive text for switching to a new heavy-weight application. -->
+ <string name="heavy_weight_switcher_text">Another application is already running
+ that must be stopped before you can start a new one.</string>
+
+ <string name="old_app_action">Return to <xliff:g id="old_app">%1$s</xliff:g></string>
+ <string name="old_app_description">Don\'t start the new application.</string>
+
+ <string name="new_app_action">Start <xliff:g id="old_app">%1$s</xliff:g></string>
+ <string name="new_app_description">Stop the old application without saving.</string>
+
<!-- Displayed in the title of the chooser for things to do with text that
- is to be sent to another application. For example, I can send text through SMS or IM. A dialog with those choices would be shown, and this would be the title. -->
+ is to be sent to another application. For example, I can send
+ text through SMS or IM. A dialog with those choices would be shown,
+ and this would be the title. -->
<string name="sendText">Select an action for text</string>
<!-- Title of the dialog where the user is adjusting the phone ringer volume -->
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 a1bc3b82..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/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index 0c7bf6f..0a7023a 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -51,6 +51,7 @@
friend class CameraSourceListener;
sp<Camera> mCamera;
+ sp<MetaData> mMeta;
Mutex mLock;
Condition mFrameAvailableCondition;
@@ -59,7 +60,6 @@
List<sp<IMemory> > mFramesBeingEncoded;
List<int64_t> mFrameTimes;
- int mWidth, mHeight;
int64_t mFirstFrameTimeUs;
int64_t mLastFrameTimestampUs;
int32_t mNumFramesReceived;
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index a9a0d55..9cddab2 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -40,6 +40,7 @@
extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
extern const char *MEDIA_MIMETYPE_CONTAINER_OGG;
extern const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA;
+extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS;
} // namespace android
diff --git a/include/media/stagefright/foundation/AAtomizer.h b/include/media/stagefright/foundation/AAtomizer.h
new file mode 100644
index 0000000..5f3a678
--- /dev/null
+++ b/include/media/stagefright/foundation/AAtomizer.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 A_ATOMIZER_H_
+
+#define A_ATOMIZER_H_
+
+#include <stdint.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/List.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct AAtomizer {
+ static const char *Atomize(const char *name);
+
+private:
+ static AAtomizer gAtomizer;
+
+ Mutex mLock;
+ Vector<List<AString> > mAtoms;
+
+ AAtomizer();
+
+ const char *atomize(const char *name);
+
+ static uint32_t Hash(const char *s);
+
+ DISALLOW_EVIL_CONSTRUCTORS(AAtomizer);
+};
+
+} // namespace android
+
+#endif // A_ATOMIZER_H_
diff --git a/services/java/com/android/server/status/StatusBarException.java b/include/media/stagefright/foundation/ABase.h
similarity index 71%
rename from services/java/com/android/server/status/StatusBarException.java
rename to include/media/stagefright/foundation/ABase.h
index be58f59..9eceea3 100644
--- a/services/java/com/android/server/status/StatusBarException.java
+++ b/include/media/stagefright/foundation/ABase.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package com.android.server.status;
+#ifndef A_BASE_H_
-public class StatusBarException extends RuntimeException {
- StatusBarException(String msg) {
- super(msg);
- }
-}
+#define A_BASE_H_
+
+#define DISALLOW_EVIL_CONSTRUCTORS(name) \
+ name(const name &); \
+ name &operator=(const name &)
+
+#endif // A_BASE_H_
diff --git a/include/media/stagefright/foundation/ABuffer.h b/include/media/stagefright/foundation/ABuffer.h
new file mode 100644
index 0000000..28f0aed
--- /dev/null
+++ b/include/media/stagefright/foundation/ABuffer.h
@@ -0,0 +1,71 @@
+/*
+ * 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 A_BUFFER_H_
+
+#define A_BUFFER_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct AMessage;
+
+struct ABuffer : public RefBase {
+ ABuffer(size_t capacity);
+ ABuffer(void *data, size_t capacity);
+
+ void setFarewellMessage(const sp<AMessage> msg);
+
+ uint8_t *base() { return (uint8_t *)mData; }
+ uint8_t *data() { return (uint8_t *)mData + mRangeOffset; }
+ size_t capacity() const { return mCapacity; }
+ size_t size() const { return mRangeLength; }
+ size_t offset() const { return mRangeOffset; }
+
+ void setRange(size_t offset, size_t size);
+
+ void setInt32Data(int32_t data) { mInt32Data = data; }
+ int32_t int32Data() const { return mInt32Data; }
+
+ sp<AMessage> meta();
+
+protected:
+ virtual ~ABuffer();
+
+private:
+ sp<AMessage> mFarewell;
+ sp<AMessage> mMeta;
+
+ void *mData;
+ size_t mCapacity;
+ size_t mRangeOffset;
+ size_t mRangeLength;
+
+ int32_t mInt32Data;
+
+ bool mOwnsData;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ABuffer);
+};
+
+} // namespace android
+
+#endif // A_BUFFER_H_
diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h
new file mode 100644
index 0000000..0f986a0
--- /dev/null
+++ b/include/media/stagefright/foundation/ADebug.h
@@ -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.
+ */
+
+#ifndef A_DEBUG_H_
+
+#define A_DEBUG_H_
+
+#include <string.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+
+namespace android {
+
+enum LogType {
+ VERBOSE,
+ INFO,
+ WARNING,
+ ERROR,
+ FATAL,
+};
+
+struct Logger {
+ Logger(LogType type);
+ virtual ~Logger();
+
+ template<class T> Logger &operator<<(const T &x) {
+ mMessage.append(x);
+
+ return *this;
+ }
+
+private:
+ android::AString mMessage;
+ LogType mLogType;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Logger);
+};
+
+const char *LeafName(const char *s);
+
+#undef LOG
+#define LOG(type) Logger(type) << LeafName(__FILE__) << ":" << __LINE__ << " "
+
+#define CHECK(condition) \
+ do { \
+ if (!(condition)) { \
+ LOG(FATAL) << "CHECK(" #condition ") failed."; \
+ } \
+ } while (false)
+
+#define MAKE_COMPARATOR(suffix,op) \
+ template<class A, class B> \
+ AString Compare_##suffix(const A &a, const B &b) { \
+ AString res; \
+ if (!(a op b)) { \
+ res.append(a); \
+ res.append(" vs. "); \
+ res.append(b); \
+ } \
+ return res; \
+ }
+
+MAKE_COMPARATOR(EQ,==)
+MAKE_COMPARATOR(NE,!=)
+MAKE_COMPARATOR(LE,<=)
+MAKE_COMPARATOR(GE,>=)
+MAKE_COMPARATOR(LT,<)
+MAKE_COMPARATOR(GT,>)
+
+#define CHECK_OP(x,y,suffix,op) \
+ do { \
+ AString ___res = Compare_##suffix(x, y); \
+ if (!___res.empty()) { \
+ LOG(FATAL) << "CHECK_" #suffix "(" #x "," #y ") failed: " \
+ << ___res; \
+ } \
+ } while (false)
+
+#define CHECK_EQ(x,y) CHECK_OP(x,y,EQ,==)
+#define CHECK_NE(x,y) CHECK_OP(x,y,NE,!=)
+#define CHECK_LE(x,y) CHECK_OP(x,y,LE,<=)
+#define CHECK_LT(x,y) CHECK_OP(x,y,LT,<)
+#define CHECK_GE(x,y) CHECK_OP(x,y,GE,>=)
+#define CHECK_GT(x,y) CHECK_OP(x,y,GT,>)
+
+#define TRESPASS() LOG(FATAL) << "Should not be here."
+
+} // namespace android
+
+#endif // A_DEBUG_H_
+
diff --git a/include/media/stagefright/foundation/AHandler.h b/include/media/stagefright/foundation/AHandler.h
new file mode 100644
index 0000000..9fccead
--- /dev/null
+++ b/include/media/stagefright/foundation/AHandler.h
@@ -0,0 +1,54 @@
+/*
+ * 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 A_HANDLER_H_
+
+#define A_HANDLER_H_
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct AMessage;
+
+struct AHandler : public RefBase {
+ AHandler()
+ : mID(0) {
+ }
+
+ ALooper::handler_id id() const {
+ return mID;
+ }
+
+protected:
+ virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
+
+private:
+ friend struct ALooperRoster;
+
+ ALooper::handler_id mID;
+
+ void setID(ALooper::handler_id id) {
+ mID = id;
+ }
+
+ DISALLOW_EVIL_CONSTRUCTORS(AHandler);
+};
+
+} // namespace android
+
+#endif // A_HANDLER_H_
diff --git a/include/media/stagefright/foundation/ALooper.h b/include/media/stagefright/foundation/ALooper.h
new file mode 100644
index 0000000..69ad837
--- /dev/null
+++ b/include/media/stagefright/foundation/ALooper.h
@@ -0,0 +1,75 @@
+/*
+ * 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 A_LOOPER_H_
+
+#define A_LOOPER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct AHandler;
+struct AMessage;
+
+struct ALooper : public RefBase {
+ typedef int32_t event_id;
+ typedef int32_t handler_id;
+
+ ALooper();
+
+ handler_id registerHandler(const sp<AHandler> &handler);
+ void unregisterHandler(handler_id handlerID);
+
+ status_t start(bool runOnCallingThread = false);
+ status_t stop();
+
+ static int64_t GetNowUs();
+
+protected:
+ virtual ~ALooper();
+
+private:
+ friend struct ALooperRoster;
+
+ struct Event {
+ int64_t mWhenUs;
+ sp<AMessage> mMessage;
+ };
+
+ Mutex mLock;
+ Condition mQueueChangedCondition;
+
+ List<Event> mEventQueue;
+
+ struct LooperThread;
+ sp<LooperThread> mThread;
+ bool mRunningLocally;
+
+ void post(const sp<AMessage> &msg, int64_t delayUs);
+ bool loop();
+
+ DISALLOW_EVIL_CONSTRUCTORS(ALooper);
+};
+
+} // namespace android
+
+#endif // A_LOOPER_H_
diff --git a/include/media/stagefright/foundation/ALooperRoster.h b/include/media/stagefright/foundation/ALooperRoster.h
new file mode 100644
index 0000000..1c6869c
--- /dev/null
+++ b/include/media/stagefright/foundation/ALooperRoster.h
@@ -0,0 +1,52 @@
+/*
+ * 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 A_LOOPER_ROSTER_H_
+
+#define A_LOOPER_ROSTER_H_
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+struct ALooperRoster {
+ ALooperRoster();
+
+ ALooper::handler_id registerHandler(
+ const sp<ALooper> looper, const sp<AHandler> &handler);
+
+ void unregisterHandler(ALooper::handler_id handlerID);
+
+ void postMessage(const sp<AMessage> &msg, int64_t delayUs = 0);
+ void deliverMessage(const sp<AMessage> &msg);
+
+private:
+ struct HandlerInfo {
+ sp<ALooper> mLooper;
+ sp<AHandler> mHandler;
+ };
+
+ Mutex mLock;
+ KeyedVector<ALooper::handler_id, HandlerInfo> mHandlers;
+ ALooper::handler_id mNextHandlerID;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ALooperRoster);
+};
+
+} // namespace android
+
+#endif // A_LOOPER_ROSTER_H_
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
new file mode 100644
index 0000000..139c620
--- /dev/null
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -0,0 +1,113 @@
+/*
+ * 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 A_MESSAGE_H_
+
+#define A_MESSAGE_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct AString;
+
+struct AMessage : public RefBase {
+ AMessage(uint32_t what = 0, ALooper::handler_id target = 0);
+
+ void setWhat(uint32_t what);
+ uint32_t what() const;
+
+ void setTarget(ALooper::handler_id target);
+ ALooper::handler_id target() const;
+
+ void setInt32(const char *name, int32_t value);
+ void setInt64(const char *name, int64_t value);
+ void setSize(const char *name, size_t value);
+ void setFloat(const char *name, float value);
+ void setDouble(const char *name, double value);
+ void setPointer(const char *name, void *value);
+ void setString(const char *name, const char *s, ssize_t len = -1);
+ void setObject(const char *name, const sp<RefBase> &obj);
+ void setMessage(const char *name, const sp<AMessage> &obj);
+
+ bool findInt32(const char *name, int32_t *value) const;
+ bool findInt64(const char *name, int64_t *value) const;
+ bool findSize(const char *name, size_t *value) const;
+ bool findFloat(const char *name, float *value) const;
+ bool findDouble(const char *name, double *value) const;
+ bool findPointer(const char *name, void **value) const;
+ bool findString(const char *name, AString *value) const;
+ bool findObject(const char *name, sp<RefBase> *obj) const;
+ bool findMessage(const char *name, sp<AMessage> *obj) const;
+
+ void post(int64_t delayUs = 0);
+
+ sp<AMessage> dup() const;
+
+protected:
+ virtual ~AMessage();
+
+private:
+ enum Type {
+ kTypeInt32,
+ kTypeInt64,
+ kTypeSize,
+ kTypeFloat,
+ kTypeDouble,
+ kTypePointer,
+ kTypeString,
+ kTypeObject,
+ kTypeMessage,
+ };
+
+ uint32_t mWhat;
+ ALooper::handler_id mTarget;
+
+ struct Item {
+ union {
+ int32_t int32Value;
+ int64_t int64Value;
+ size_t sizeValue;
+ float floatValue;
+ double doubleValue;
+ void *ptrValue;
+ RefBase *refValue;
+ AString *stringValue;
+ } u;
+ const char *mName;
+ Type mType;
+ };
+
+ enum {
+ kMaxNumItems = 16
+ };
+ Item mItems[kMaxNumItems];
+ size_t mNumItems;
+
+ void clear();
+ Item *allocateItem(const char *name);
+ void freeItem(Item *item);
+ const Item *findItem(const char *name, Type type) const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AMessage);
+};
+
+} // namespace android
+
+#endif // A_MESSAGE_H_
diff --git a/include/media/stagefright/foundation/AString.h b/include/media/stagefright/foundation/AString.h
new file mode 100644
index 0000000..55ade64
--- /dev/null
+++ b/include/media/stagefright/foundation/AString.h
@@ -0,0 +1,94 @@
+/*
+ * 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 A_STRING_H_
+
+#define A_STRING_H_
+
+#include <sys/types.h>
+
+namespace android {
+
+struct AString {
+ AString();
+ AString(const char *s);
+ AString(const char *s, size_t size);
+ AString(const AString &from);
+ AString(const AString &from, size_t offset, size_t n);
+ ~AString();
+
+ AString &operator=(const AString &from);
+ void setTo(const char *s);
+ void setTo(const char *s, size_t size);
+ void setTo(const AString &from, size_t offset, size_t n);
+
+ size_t size() const;
+ const char *c_str() const;
+
+ bool empty() const;
+
+ void clear();
+ void trim();
+ void erase(size_t start, size_t n);
+
+ void append(char c) { append(&c, 1); }
+ void append(const char *s);
+ void append(const char *s, size_t size);
+ void append(const AString &from);
+ void append(const AString &from, size_t offset, size_t n);
+ void append(int x);
+ void append(unsigned x);
+ void append(long x);
+ void append(unsigned long x);
+ void append(long long x);
+ void append(unsigned long long x);
+ void append(float x);
+ void append(double x);
+ void append(void *x);
+
+ void insert(const AString &from, size_t insertionPos);
+ void insert(const char *from, size_t size, size_t insertionPos);
+
+ ssize_t find(const char *substring, size_t start = 0) const;
+
+ size_t hash() const;
+
+ bool operator==(const AString &other) const;
+ bool operator<(const AString &other) const;
+ bool operator>(const AString &other) const;
+
+ int compare(const AString &other) const;
+
+ bool startsWith(const char *prefix) const;
+
+ void tolower();
+
+private:
+ static const char *kEmptyString;
+
+ char *mData;
+ size_t mSize;
+ size_t mAllocSize;
+
+ void makeMutable();
+};
+
+AString StringPrintf(const char *format, ...);
+
+} // namespace android
+
+#endif // A_STRING_H_
+
diff --git a/services/java/com/android/server/status/StatusBarNotification.java b/include/media/stagefright/foundation/base64.h
similarity index 62%
rename from services/java/com/android/server/status/StatusBarNotification.java
rename to include/media/stagefright/foundation/base64.h
index e5773f7..e340b89 100644
--- a/services/java/com/android/server/status/StatusBarNotification.java
+++ b/include/media/stagefright/foundation/base64.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,20 @@
* limitations under the License.
*/
-package com.android.server.status;
+#ifndef BASE_64_H_
-import android.os.IBinder;
-import android.view.View;
+#define BASE_64_H_
-class StatusBarNotification {
- IBinder key;
- NotificationData data;
- View view;
- View contentView;
-}
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct AString;
+
+sp<ABuffer> decodeBase64(const AString &s);
+void encodeBase64(const void *data, size_t size, AString *out);
+
+} // namespace android
+
+#endif // BASE_64_H_
diff --git a/services/java/com/android/server/status/StatusBarException.java b/include/media/stagefright/foundation/hexdump.h
similarity index 70%
copy from services/java/com/android/server/status/StatusBarException.java
copy to include/media/stagefright/foundation/hexdump.h
index be58f59..f6083a9 100644
--- a/services/java/com/android/server/status/StatusBarException.java
+++ b/include/media/stagefright/foundation/hexdump.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,10 +14,16 @@
* limitations under the License.
*/
-package com.android.server.status;
+#ifndef HEXDUMP_H_
-public class StatusBarException extends RuntimeException {
- StatusBarException(String msg) {
- super(msg);
- }
-}
+#define HEXDUMP_H_
+
+#include <sys/types.h>
+
+namespace android {
+
+void hexdump(const void *_data, size_t size);
+
+} // namespace android
+
+#endif // HEXDUMP_H_
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/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index dcce25e..c11c855 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -122,7 +122,8 @@
volatile int8_t index[NUM_BUFFER_MAX];
int32_t identity; // surface's identity (const)
- int32_t reserved32[2];
+ int32_t token; // surface's token (for debugging)
+ int32_t reserved32[1];
Statistics stats;
int32_t reserved;
BufferData buffers[NUM_BUFFER_MAX]; // 960 bytes
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index 3271cfd..dd44aa5 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -85,9 +85,12 @@
/* create connection with surface flinger, requires
* ACCESS_SURFACE_FLINGER permission
*/
-
virtual sp<ISurfaceComposerClient> createConnection() = 0;
+ /* create a client connection with surface flinger
+ */
+ virtual sp<ISurfaceComposerClient> createClientConnection() = 0;
+
/* retrieve the control block */
virtual sp<IMemoryHeap> getCblk() const = 0;
@@ -123,6 +126,7 @@
// Java by ActivityManagerService.
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
CREATE_CONNECTION,
+ CREATE_CLIENT_CONNECTION,
GET_CBLK,
OPEN_GLOBAL_TRANSACTION,
CLOSE_GLOBAL_TRANSACTION,
diff --git a/include/surfaceflinger/ISurfaceComposerClient.h b/include/surfaceflinger/ISurfaceComposerClient.h
index b2a4766..a1e9e04 100644
--- a/include/surfaceflinger/ISurfaceComposerClient.h
+++ b/include/surfaceflinger/ISurfaceComposerClient.h
@@ -58,6 +58,7 @@
};
virtual sp<IMemoryHeap> getControlBlock() const = 0;
+ virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const = 0;
/*
* Requires ACCESS_SURFACE_FLINGER permission
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 2957970..ac01ce5 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -145,15 +145,13 @@
uint32_t reserved[2];
};
- Surface(const Parcel& data);
+ static sp<Surface> readFromParcel(
+ const Parcel& data, const sp<Surface>& other);
static bool isValid(const sp<Surface>& surface) {
return (surface != 0) && surface->isValid();
}
- static bool isSameSurface(
- const sp<Surface>& lhs, const sp<Surface>& rhs);
-
bool isValid();
SurfaceID ID() const { return mToken; }
uint32_t getFlags() const { return mFlags; }
@@ -191,6 +189,7 @@
Surface(const Surface& rhs);
Surface(const sp<SurfaceControl>& control);
+ Surface(const Parcel& data, const sp<IBinder>& ref);
~Surface();
@@ -229,7 +228,6 @@
*/
void init();
status_t validate() const;
- status_t initCheck() const;
sp<ISurface> getISurface() const;
inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; }
@@ -264,15 +262,15 @@
};
// constants
- sp<SurfaceClient> mClient;
+ GraphicBufferMapper& mBufferMapper;
+ SurfaceClient& mClient;
+ SharedBufferClient* mSharedBufferClient;
+ status_t mInitCheck;
sp<ISurface> mSurface;
SurfaceID mToken;
uint32_t mIdentity;
PixelFormat mFormat;
uint32_t mFlags;
- GraphicBufferMapper& mBufferMapper;
- SharedBufferClient* mSharedBufferClient;
- status_t mInitCheck;
// protected by mSurfaceLock
Rect mSwapRectangle;
diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h
index 8e28a81..8773d71 100644
--- a/include/surfaceflinger/SurfaceComposerClient.h
+++ b/include/surfaceflinger/SurfaceComposerClient.h
@@ -22,8 +22,9 @@
#include <binder/IBinder.h>
-#include <utils/SortedVector.h>
#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+#include <utils/SortedVector.h>
#include <utils/threads.h>
#include <ui/PixelFormat.h>
@@ -39,6 +40,22 @@
class SharedClient;
class ISurfaceComposer;
class DisplayInfo;
+class surface_flinger_cblk_t;
+
+// ---------------------------------------------------------------------------
+
+class ComposerService : public Singleton<ComposerService>
+{
+ // these are constants
+ sp<ISurfaceComposer> mComposerService;
+ sp<IMemoryHeap> mServerCblkMemory;
+ surface_flinger_cblk_t volatile* mServerCblk;
+ ComposerService();
+ friend class Singleton<ComposerService>;
+public:
+ static sp<ISurfaceComposer> getComposerService();
+ static surface_flinger_cblk_t const volatile * getControlBlock();
+};
// ---------------------------------------------------------------------------
@@ -153,25 +170,6 @@
};
// ---------------------------------------------------------------------------
-
-class SurfaceClient : public RefBase
-{
- // all these attributes are constants
- status_t mStatus;
- SharedClient* mControl;
- sp<IMemoryHeap> mControlMemory;
- sp<IBinder> mConnection;
- sp<ISurfaceComposer> mComposerService;
- void init(const sp<IBinder>& conn);
-public:
- explicit SurfaceClient(const sp<IBinder>& conn);
- explicit SurfaceClient(const sp<SurfaceComposerClient>& client);
- status_t initCheck() const;
- SharedClient* getSharedClient() const;
- void signalServer() const;
-};
-
-// ---------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_SF_SURFACE_COMPOSER_CLIENT_H
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index e72b6b3..a3e85a9 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -93,10 +93,8 @@
void setIndex(int index);
int getIndex() const;
- void setVerticalStride(uint32_t vstride);
- uint32_t getVerticalStride() const;
-protected:
+private:
virtual ~GraphicBuffer();
enum {
@@ -105,8 +103,12 @@
ownData = 2,
};
- inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; }
- inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; }
+ inline const GraphicBufferMapper& getBufferMapper() const {
+ return mBufferMapper;
+ }
+ inline GraphicBufferMapper& getBufferMapper() {
+ return mBufferMapper;
+ }
uint8_t mOwner;
private:
@@ -134,7 +136,6 @@
GraphicBufferMapper& mBufferMapper;
ssize_t mInitCheck;
- uint32_t mVStride;
int mIndex;
};
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/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk
index b8a0630..0879a66 100644
--- a/libs/surfaceflinger/Android.mk
+++ b/libs/surfaceflinger/Android.mk
@@ -14,7 +14,6 @@
MessageQueue.cpp \
SurfaceFlinger.cpp \
TextureManager.cpp \
- Tokenizer.cpp \
Transform.cpp
LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\"
diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp
index 3fbb4d3..a94fdd4 100644
--- a/libs/surfaceflinger/Layer.cpp
+++ b/libs/surfaceflinger/Layer.cpp
@@ -47,24 +47,16 @@
// ---------------------------------------------------------------------------
-Layer::Layer(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i)
- : LayerBaseClient(flinger, display, client, i),
- lcblk(NULL),
- mSecure(false),
+Layer::Layer(SurfaceFlinger* flinger,
+ DisplayID display, const sp<Client>& client)
+ : LayerBaseClient(flinger, display, client),
mNeedsBlending(true),
mNeedsDithering(false),
+ mSecure(false),
mTextureManager(mFlags),
mBufferManager(mTextureManager),
mWidth(0), mHeight(0), mFixedSize(false)
{
- // no OpenGL operation is possible here, since we might not be
- // in the OpenGL thread.
- lcblk = new SharedBufferServer(
- client->ctrlblk, i, mBufferManager.getDefaultBufferCount(),
- getIdentity());
-
- mBufferManager.setActiveBufferIndex( lcblk->getFrontBuffer() );
}
Layer::~Layer()
@@ -73,16 +65,46 @@
EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
mBufferManager.destroy(dpy);
- // the actual buffers will be destroyed here
- delete lcblk;
+ // we can use getUserClientUnsafe here because we know we're
+ // single-threaded at that point.
+ sp<UserClient> ourClient(mUserClientRef.getUserClientUnsafe());
+ if (ourClient != 0) {
+ ourClient->detachLayer(this);
+ }
+}
+
+status_t Layer::setToken(const sp<UserClient>& userClient,
+ SharedClient* sharedClient, int32_t token)
+{
+ SharedBufferServer* lcblk = new SharedBufferServer(
+ sharedClient, token, mBufferManager.getDefaultBufferCount(),
+ getIdentity());
+
+ status_t err = mUserClientRef.setToken(userClient, lcblk, token);
+ if (err != NO_ERROR) {
+ LOGE("ClientRef::setToken(%p, %p, %u) failed",
+ userClient.get(), lcblk, token);
+ delete lcblk;
+ }
+
+ return err;
+}
+
+int32_t Layer::getToken() const
+{
+ return mUserClientRef.getToken();
}
// called with SurfaceFlinger::mStateLock as soon as the layer is entered
// in the purgatory list
void Layer::onRemoved()
{
- // wake up the condition
- lcblk->setStatus(NO_INIT);
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (lcblk) {
+ // wake up the condition
+ lcblk->setStatus(NO_INIT);
+ }
}
sp<LayerBaseClient::Surface> Layer::createSurface() const
@@ -140,7 +162,7 @@
int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED);
mNeedsDithering = layerRedsize > displayRedSize;
- mSurface = new SurfaceLayer(mFlinger, clientIndex(), this);
+ mSurface = new SurfaceLayer(mFlinger, this);
return NO_ERROR;
}
@@ -225,10 +247,9 @@
status_t Layer::setBufferCount(int bufferCount)
{
- // Ensures our client doesn't go away while we're accessing
- // the shared area.
- sp<Client> ourClient(client.promote());
- if (ourClient == 0) {
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (!lcblk) {
// oops, the client is already gone
return DEAD_OBJECT;
}
@@ -247,7 +268,7 @@
{
sp<GraphicBuffer> buffer;
- if ((reqWidth | reqHeight | reqFormat) < 0)
+ if (int32_t(reqWidth | reqHeight | reqFormat) < 0)
return buffer;
if ((!reqWidth && reqHeight) || (reqWidth && !reqHeight))
@@ -255,8 +276,9 @@
// this ensures our client doesn't go away while we're accessing
// the shared area.
- sp<Client> ourClient(client.promote());
- if (ourClient == 0) {
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (!lcblk) {
// oops, the client is already gone
return buffer;
}
@@ -391,8 +413,12 @@
// a buffer, it'll get the new size.
setBufferSize(temp.requested_w, temp.requested_h);
- // all buffers need reallocation
- lcblk->reallocateAll();
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (lcblk) {
+ // all buffers need reallocation
+ lcblk->reallocateAll();
+ }
} else {
// record the new size
setBufferSize(temp.requested_w, temp.requested_h);
@@ -427,6 +453,14 @@
void Layer::lockPageFlip(bool& recomputeVisibleRegions)
{
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (!lcblk) {
+ // client died
+ recomputeVisibleRegions = true;
+ return;
+ }
+
ssize_t buf = lcblk->retireAndLock();
if (buf == NOT_ENOUGH_DATA) {
// NOTE: This is not an error, it simply means there is nothing to
@@ -436,14 +470,14 @@
}
if (buf < NO_ERROR) {
- LOGE("retireAndLock() buffer index (%d) out of range", buf);
+ LOGE("retireAndLock() buffer index (%d) out of range", int(buf));
mPostedDirtyRegion.clear();
return;
}
// we retired a buffer, which becomes the new front buffer
if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) {
- LOGE("retireAndLock() buffer index (%d) out of range", buf);
+ LOGE("retireAndLock() buffer index (%d) out of range", int(buf));
mPostedDirtyRegion.clear();
return;
}
@@ -538,9 +572,17 @@
void Layer::finishPageFlip()
{
- int buf = mBufferManager.getActiveBufferIndex();
- status_t err = lcblk->unlock( buf );
- LOGE_IF(err!=NO_ERROR, "layer %p, buffer=%d wasn't locked!", this, buf);
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (lcblk) {
+ int buf = mBufferManager.getActiveBufferIndex();
+ if (buf >= 0) {
+ status_t err = lcblk->unlock( buf );
+ LOGE_IF(err!=NO_ERROR,
+ "layer %p, buffer=%d wasn't locked!",
+ this, buf);
+ }
+ }
}
@@ -548,8 +590,15 @@
{
LayerBaseClient::dump(result, buffer, SIZE);
- SharedBufferStack::Statistics stats = lcblk->getStats();
- result.append( lcblk->dump(" ") );
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ uint32_t totalTime = 0;
+ if (lcblk) {
+ SharedBufferStack::Statistics stats = lcblk->getStats();
+ totalTime= stats.totalTime;
+ result.append( lcblk->dump(" ") );
+ }
+
sp<const GraphicBuffer> buf0(getBuffer(0));
sp<const GraphicBuffer> buf1(getBuffer(1));
uint32_t w0=0, h0=0, s0=0;
@@ -568,18 +617,59 @@
" "
"format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u],"
" freezeLock=%p, dq-q-time=%u us\n",
- pixelFormat(),
- w0, h0, s0, w1, h1, s1,
- getFreezeLock().get(), stats.totalTime);
+ mFormat, w0, h0, s0, w1, h1, s1,
+ getFreezeLock().get(), totalTime);
result.append(buffer);
}
// ---------------------------------------------------------------------------
+Layer::ClientRef::ClientRef()
+ : mToken(-1) {
+}
+
+Layer::ClientRef::~ClientRef() {
+ delete lcblk;
+}
+
+int32_t Layer::ClientRef::getToken() const {
+ Mutex::Autolock _l(mLock);
+ return mToken;
+}
+
+status_t Layer::ClientRef::setToken(const sp<UserClient>& uc,
+ SharedBufferServer* sharedClient, int32_t token) {
+ Mutex::Autolock _l(mLock);
+ if (mToken >= 0)
+ return INVALID_OPERATION;
+ mUserClient = uc;
+ mToken = token;
+ lcblk = sharedClient;
+ return NO_ERROR;
+}
+
+sp<UserClient> Layer::ClientRef::getUserClientUnsafe() const {
+ return mUserClient.promote();
+}
+
+// this class gives us access to SharedBufferServer safely
+// it makes sure the UserClient (and its associated shared memory)
+// won't go away while we're accessing it.
+Layer::ClientRef::Access::Access(const ClientRef& ref)
+ : lcblk(0)
+{
+ Mutex::Autolock _l(ref.mLock);
+ mUserClientStrongRef = ref.mUserClient.promote();
+ if (mUserClientStrongRef != 0)
+ lcblk = ref.lcblk;
+}
+
+// ---------------------------------------------------------------------------
+
Layer::BufferManager::BufferManager(TextureManager& tm)
: mNumBuffers(NUM_BUFFERS), mTextureManager(tm),
- mActiveBuffer(0), mFailover(false)
+ mActiveBuffer(-1), mFailover(false)
{
}
@@ -600,7 +690,6 @@
}
status_t Layer::BufferManager::setActiveBufferIndex(size_t index) {
- // TODO: need to validate 'index'
mActiveBuffer = index;
return NO_ERROR;
}
@@ -611,7 +700,7 @@
Texture Layer::BufferManager::getActiveTexture() const {
Texture res;
- if (mFailover) {
+ if (mFailover || mActiveBuffer<0) {
res = mFailoverTexture;
} else {
static_cast<Image&>(res) = mBufferData[mActiveBuffer].texture;
@@ -620,10 +709,14 @@
}
sp<GraphicBuffer> Layer::BufferManager::getActiveBuffer() const {
- const size_t activeBuffer = mActiveBuffer;
- BufferData const * const buffers = mBufferData;
- Mutex::Autolock _l(mLock);
- return buffers[activeBuffer].buffer;
+ sp<GraphicBuffer> result;
+ const ssize_t activeBuffer = mActiveBuffer;
+ if (activeBuffer >= 0) {
+ BufferData const * const buffers = mBufferData;
+ Mutex::Autolock _l(mLock);
+ result = buffers[activeBuffer].buffer;
+ }
+ return result;
}
sp<GraphicBuffer> Layer::BufferManager::detachBuffer(size_t index)
@@ -667,19 +760,22 @@
status_t Layer::BufferManager::initEglImage(EGLDisplay dpy,
const sp<GraphicBuffer>& buffer)
{
- size_t index = mActiveBuffer;
- Image& texture(mBufferData[index].texture);
- status_t err = mTextureManager.initEglImage(&texture, dpy, buffer);
- // if EGLImage fails, we switch to regular texture mode, and we
- // free all resources associated with using EGLImages.
- if (err == NO_ERROR) {
- mFailover = false;
- destroyTexture(&mFailoverTexture, dpy);
- } else {
- mFailover = true;
- const size_t num = mNumBuffers;
- for (size_t i=0 ; i<num ; i++) {
- destroyTexture(&mBufferData[i].texture, dpy);
+ status_t err = NO_INIT;
+ ssize_t index = mActiveBuffer;
+ if (index >= 0) {
+ Image& texture(mBufferData[index].texture);
+ err = mTextureManager.initEglImage(&texture, dpy, buffer);
+ // if EGLImage fails, we switch to regular texture mode, and we
+ // free all resources associated with using EGLImages.
+ if (err == NO_ERROR) {
+ mFailover = false;
+ destroyTexture(&mFailoverTexture, dpy);
+ } else {
+ mFailover = true;
+ const size_t num = mNumBuffers;
+ for (size_t i=0 ; i<num ; i++) {
+ destroyTexture(&mBufferData[i].texture, dpy);
+ }
}
}
return err;
@@ -707,8 +803,8 @@
// ---------------------------------------------------------------------------
Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger,
- SurfaceID id, const sp<Layer>& owner)
- : Surface(flinger, id, owner->getIdentity(), owner)
+ const sp<Layer>& owner)
+ : Surface(flinger, owner->getIdentity(), owner)
{
}
diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h
index 59603a5..d396ecf 100644
--- a/libs/surfaceflinger/Layer.h
+++ b/libs/surfaceflinger/Layer.h
@@ -38,6 +38,7 @@
// ---------------------------------------------------------------------------
class Client;
+class UserClient;
class FreezeLock;
// ---------------------------------------------------------------------------
@@ -45,21 +46,26 @@
class Layer : public LayerBaseClient
{
public:
- // lcblk is (almost) only accessed from the main SF thread, in the places
- // where it's not, a reference to Client must be held
- SharedBufferServer* lcblk;
+ Layer(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client);
- Layer(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i);
+ virtual ~Layer();
- virtual ~Layer();
+ virtual const char* getTypeId() const { return "Layer"; }
+ // the this layer's size and format
status_t setBuffers(uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags=0);
+ // associate a UserClient to this Layer
+ status_t setToken(const sp<UserClient>& uc, SharedClient* sc, int32_t idx);
+ int32_t getToken() const;
+
+ // Set this Layer's buffers size
void setBufferSize(uint32_t w, uint32_t h);
bool isFixedSize() const;
+ // LayerBase interface
virtual void onDraw(const Region& clip) const;
virtual uint32_t doTransaction(uint32_t transactionFlags);
virtual void lockPageFlip(bool& recomputeVisibleRegions);
@@ -72,32 +78,29 @@
virtual sp<Surface> createSurface() const;
virtual status_t ditch();
virtual void onRemoved();
-
- // only for debugging
- inline sp<GraphicBuffer> getBuffer(int i) const { return mBufferManager.getBuffer(i); }
- // only for debugging
- inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; }
- // only for debugging
- inline PixelFormat pixelFormat() const { return mFormat; }
- virtual const char* getTypeId() const { return "Layer"; }
+ // only for debugging
+ inline sp<GraphicBuffer> getBuffer(int i) const {
+ return mBufferManager.getBuffer(i); }
+ // only for debugging
+ inline const sp<FreezeLock>& getFreezeLock() const {
+ return mFreezeLock; }
protected:
virtual void dump(String8& result, char* scratch, size_t size) const;
private:
void reloadTexture(const Region& dirty);
-
uint32_t getEffectiveUsage(uint32_t usage) const;
-
sp<GraphicBuffer> requestBuffer(int bufferIdx,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
status_t setBufferCount(int bufferCount);
+ // -----------------------------------------------------------------------
+
class SurfaceLayer : public LayerBaseClient::Surface {
public:
- SurfaceLayer(const sp<SurfaceFlinger>& flinger,
- SurfaceID id, const sp<Layer>& owner);
+ SurfaceLayer(const sp<SurfaceFlinger>& flinger, const sp<Layer>& owner);
~SurfaceLayer();
private:
virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
@@ -108,87 +111,120 @@
}
};
friend class SurfaceLayer;
-
- sp<Surface> mSurface;
- bool mSecure;
- int32_t mFrontBufferIndex;
- bool mNeedsBlending;
- bool mNeedsDithering;
- Region mPostedDirtyRegion;
- sp<FreezeLock> mFreezeLock;
- PixelFormat mFormat;
+ // -----------------------------------------------------------------------
- class BufferManager {
- static const size_t NUM_BUFFERS = 2;
- struct BufferData {
- sp<GraphicBuffer> buffer;
- Image texture;
- };
- // this lock protect mBufferData[].buffer but since there
- // is very little contention, we have only one like for
- // the whole array, we also use it to protect mNumBuffers.
- mutable Mutex mLock;
- BufferData mBufferData[SharedBufferStack::NUM_BUFFER_MAX];
- size_t mNumBuffers;
- Texture mFailoverTexture;
- TextureManager& mTextureManager;
- ssize_t mActiveBuffer;
- bool mFailover;
- static status_t destroyTexture(Image* tex, EGLDisplay dpy);
+ class ClientRef {
+ ClientRef(const ClientRef& rhs);
+ ClientRef& operator = (const ClientRef& rhs);
+ mutable Mutex mLock;
+ // binder thread, page-flip thread
+ SharedBufferServer* lcblk;
+ wp<UserClient> mUserClient;
+ int32_t mToken;
+ public:
+ ClientRef();
+ ~ClientRef();
+ int32_t getToken() const;
+ status_t setToken(const sp<UserClient>& uc,
+ SharedBufferServer* sharedClient, int32_t token);
+ sp<UserClient> getUserClientUnsafe() const;
+ class Access {
+ Access(const Access& rhs);
+ Access& operator = (const Access& rhs);
+ sp<UserClient> mUserClientStrongRef;
+ SharedBufferServer* lcblk;
+ public:
+ Access(const ClientRef& ref);
+ inline SharedBufferServer* get() const { return lcblk; }
+ };
+ friend class Access;
+ };
- public:
- static size_t getDefaultBufferCount() { return NUM_BUFFERS; }
- BufferManager(TextureManager& tm);
- ~BufferManager();
+ // -----------------------------------------------------------------------
- // detach/attach buffer from/to given index
- sp<GraphicBuffer> detachBuffer(size_t index);
- status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer);
+ class BufferManager {
+ static const size_t NUM_BUFFERS = 2;
+ struct BufferData {
+ sp<GraphicBuffer> buffer;
+ Image texture;
+ };
+ // this lock protect mBufferData[].buffer but since there
+ // is very little contention, we have only one like for
+ // the whole array, we also use it to protect mNumBuffers.
+ mutable Mutex mLock;
+ BufferData mBufferData[SharedBufferStack::NUM_BUFFER_MAX];
+ size_t mNumBuffers;
+ Texture mFailoverTexture;
+ TextureManager& mTextureManager;
+ ssize_t mActiveBuffer;
+ bool mFailover;
+ static status_t destroyTexture(Image* tex, EGLDisplay dpy);
- // resize the number of active buffers
- status_t resize(size_t size);
+ public:
+ static size_t getDefaultBufferCount() { return NUM_BUFFERS; }
+ BufferManager(TextureManager& tm);
+ ~BufferManager();
- // ----------------------------------------------
- // must be called from GL thread
+ // detach/attach buffer from/to given index
+ sp<GraphicBuffer> detachBuffer(size_t index);
+ status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer);
+ // resize the number of active buffers
+ status_t resize(size_t size);
- // set/get active buffer index
- status_t setActiveBufferIndex(size_t index);
- size_t getActiveBufferIndex() const;
+ // ----------------------------------------------
+ // must be called from GL thread
- // return the active buffer
- sp<GraphicBuffer> getActiveBuffer() const;
+ // set/get active buffer index
+ status_t setActiveBufferIndex(size_t index);
+ size_t getActiveBufferIndex() const;
+ // return the active buffer
+ sp<GraphicBuffer> getActiveBuffer() const;
+ // return the active texture (or fail-over)
+ Texture getActiveTexture() const;
+ // frees resources associated with all buffers
+ status_t destroy(EGLDisplay dpy);
+ // load bitmap data into the active buffer
+ status_t loadTexture(const Region& dirty, const GGLSurface& t);
+ // make active buffer an EGLImage if needed
+ status_t initEglImage(EGLDisplay dpy,
+ const sp<GraphicBuffer>& buffer);
- // return the active texture (or fail-over)
- Texture getActiveTexture() const;
+ // ----------------------------------------------
+ // only for debugging
+ sp<GraphicBuffer> getBuffer(size_t index) const;
+ };
- // frees resources associated with all buffers
- status_t destroy(EGLDisplay dpy);
+ // -----------------------------------------------------------------------
- // load bitmap data into the active buffer
- status_t loadTexture(const Region& dirty, const GGLSurface& t);
+ // thread-safe
+ ClientRef mUserClientRef;
- // make active buffer an EGLImage if needed
- status_t initEglImage(EGLDisplay dpy,
- const sp<GraphicBuffer>& buffer);
+ // constants
+ sp<Surface> mSurface;
+ PixelFormat mFormat;
+ bool mNeedsBlending;
+ bool mNeedsDithering;
- // ----------------------------------------------
- // only for debugging
- sp<GraphicBuffer> getBuffer(size_t index) const;
- };
+ // page-flip thread (currently main thread)
+ bool mSecure;
+ Region mPostedDirtyRegion;
- TextureManager mTextureManager;
- BufferManager mBufferManager;
+ // page-flip thread and transaction thread (currently main thread)
+ sp<FreezeLock> mFreezeLock;
- // this lock protects mWidth and mHeight which are accessed from
- // the main thread and requestBuffer's binder transaction thread.
- mutable Mutex mLock;
- uint32_t mWidth;
- uint32_t mHeight;
- uint32_t mReqWidth;
- uint32_t mReqHeight;
- uint32_t mReqFormat;
- bool mFixedSize;
+ // see threading usage in declaration
+ TextureManager mTextureManager;
+ BufferManager mBufferManager;
+
+ // binder thread, transaction thread
+ mutable Mutex mLock;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mReqWidth;
+ uint32_t mReqHeight;
+ uint32_t mReqFormat;
+ bool mFixedSize;
};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp
index 7ab74b4..1f66fd0 100644
--- a/libs/surfaceflinger/LayerBase.cpp
+++ b/libs/surfaceflinger/LayerBase.cpp
@@ -151,7 +151,6 @@
return true;
}
bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) {
- // TODO: check the matrix has changed
mCurrentState.sequence++;
mCurrentState.transform.set(
matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy);
@@ -159,7 +158,6 @@
return true;
}
bool LayerBase::setTransparentRegionHint(const Region& transparent) {
- // TODO: check the region has changed
mCurrentState.sequence++;
mCurrentState.transparentRegion = transparent;
requestTransaction();
@@ -488,37 +486,20 @@
int32_t LayerBaseClient::sIdentity = 1;
LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i)
- : LayerBase(flinger, display), client(client), mIndex(i),
+ const sp<Client>& client)
+ : LayerBase(flinger, display), mClientRef(client),
mIdentity(uint32_t(android_atomic_inc(&sIdentity)))
{
}
-void LayerBaseClient::onFirstRef()
-{
- sp<Client> client(this->client.promote());
- if (client != 0) {
- client->bindLayer(this, mIndex);
- }
-}
-
LayerBaseClient::~LayerBaseClient()
{
- sp<Client> client(this->client.promote());
- if (client != 0) {
- client->free(mIndex);
+ sp<Client> c(mClientRef.promote());
+ if (c != 0) {
+ c->detachLayer(this);
}
}
-ssize_t LayerBaseClient::serverIndex() const
-{
- sp<Client> client(this->client.promote());
- if (client != 0) {
- return (client->cid<<16)|mIndex;
- }
- return ssize_t(0xFFFF0000 | mIndex);
-}
-
sp<LayerBaseClient::Surface> LayerBaseClient::getSurface()
{
sp<Surface> s;
@@ -533,7 +514,7 @@
sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const
{
- return new Surface(mFlinger, clientIndex(), mIdentity,
+ return new Surface(mFlinger, mIdentity,
const_cast<LayerBaseClient *>(this));
}
@@ -541,13 +522,12 @@
{
LayerBase::dump(result, buffer, SIZE);
- sp<Client> client(this->client.promote());
+ sp<Client> client(mClientRef.promote());
snprintf(buffer, SIZE,
" name=%s\n"
- " id=0x%08x, client=0x%08x, identity=%u\n",
+ " client=%p, identity=%u\n",
getName().string(),
- clientIndex(), client.get() ? client->cid : 0,
- getIdentity());
+ client.get(), getIdentity());
result.append(buffer);
}
@@ -556,9 +536,9 @@
LayerBaseClient::Surface::Surface(
const sp<SurfaceFlinger>& flinger,
- SurfaceID id, int identity,
+ int identity,
const sp<LayerBaseClient>& owner)
- : mFlinger(flinger), mToken(id), mIdentity(identity), mOwner(owner)
+ : mFlinger(flinger), mIdentity(identity), mOwner(owner)
{
}
diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h
index 2e2f2df..1a07f32 100644
--- a/libs/surfaceflinger/LayerBase.h
+++ b/libs/surfaceflinger/LayerBase.h
@@ -45,6 +45,7 @@
class Client;
class GraphicBuffer;
class GraphicPlane;
+class LayerBaseClient;
class SurfaceFlinger;
class Texture;
@@ -100,8 +101,9 @@
void invalidate();
+ virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; }
+
virtual const char* getTypeId() const { return "LayerBase"; }
- virtual ssize_t serverIndex() const { return -1; }
/**
* draw - performs some global clipping optimizations
@@ -269,30 +271,24 @@
public:
class Surface;
- LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i);
+ LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client);
virtual ~LayerBaseClient();
- virtual void onFirstRef();
- const wp<Client> client;
-
- inline uint32_t getIdentity() const { return mIdentity; }
- inline int32_t clientIndex() const { return mIndex; }
-
sp<Surface> getSurface();
virtual sp<Surface> createSurface() const;
- virtual ssize_t serverIndex() const;
+ virtual sp<LayerBaseClient> getLayerBaseClient() const {
+ return const_cast<LayerBaseClient*>(this); }
virtual const char* getTypeId() const { return "LayerBaseClient"; }
- class Surface : public BnSurface
- {
+ uint32_t getIdentity() const { return mIdentity; }
+
+ class Surface : public BnSurface {
public:
- int32_t getToken() const { return mToken; }
int32_t getIdentity() const { return mIdentity; }
protected:
- Surface(const sp<SurfaceFlinger>& flinger,
- SurfaceID id, int identity,
+ Surface(const sp<SurfaceFlinger>& flinger, int identity,
const sp<LayerBaseClient>& owner);
virtual ~Surface();
virtual status_t onTransact(uint32_t code, const Parcel& data,
@@ -313,7 +309,6 @@
protected:
friend class LayerBaseClient;
sp<SurfaceFlinger> mFlinger;
- int32_t mToken;
int32_t mIdentity;
wp<LayerBaseClient> mOwner;
};
@@ -324,12 +319,12 @@
virtual void dump(String8& result, char* scratch, size_t size) const;
private:
- int32_t mIndex;
- mutable Mutex mLock;
- mutable wp<Surface> mClientSurface;
+ mutable Mutex mLock;
+ mutable wp<Surface> mClientSurface;
+ const wp<Client> mClientRef;
// only read
- const uint32_t mIdentity;
- static int32_t sIdentity;
+ const uint32_t mIdentity;
+ static int32_t sIdentity;
};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp
index 09c90e8..4c8bae8 100644
--- a/libs/surfaceflinger/LayerBlur.cpp
+++ b/libs/surfaceflinger/LayerBlur.cpp
@@ -34,8 +34,8 @@
// ---------------------------------------------------------------------------
LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i)
- : LayerBaseClient(flinger, display, client, i), mCacheDirty(true),
+ const sp<Client>& client)
+ : LayerBaseClient(flinger, display, client), mCacheDirty(true),
mRefreshCache(true), mCacheAge(0), mTextureName(-1U),
mWidthScale(1.0f), mHeightScale(1.0f),
mBlurFormat(GGL_PIXEL_FORMAT_RGB_565)
diff --git a/libs/surfaceflinger/LayerBlur.h b/libs/surfaceflinger/LayerBlur.h
index 380f587..4c9ec64 100644
--- a/libs/surfaceflinger/LayerBlur.h
+++ b/libs/surfaceflinger/LayerBlur.h
@@ -32,7 +32,7 @@
{
public:
LayerBlur(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i);
+ const sp<Client>& client);
virtual ~LayerBlur();
virtual void onDraw(const Region& clip) const;
diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp
index 8a582da..732a4ec 100644
--- a/libs/surfaceflinger/LayerBuffer.cpp
+++ b/libs/surfaceflinger/LayerBuffer.cpp
@@ -44,8 +44,8 @@
// ---------------------------------------------------------------------------
LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i)
- : LayerBaseClient(flinger, display, client, i),
+ const sp<Client>& client)
+ : LayerBaseClient(flinger, display, client),
mNeedsBlending(false), mBlitEngine(0)
{
}
@@ -60,8 +60,7 @@
void LayerBuffer::onFirstRef()
{
LayerBaseClient::onFirstRef();
- mSurface = new SurfaceLayerBuffer(mFlinger, clientIndex(),
- const_cast<LayerBuffer *>(this));
+ mSurface = new SurfaceLayerBuffer(mFlinger, this);
hw_module_t const* module = (hw_module_t const*)sGrallocModule;
if (!module) {
@@ -204,9 +203,9 @@
// LayerBuffer::SurfaceLayerBuffer
// ============================================================================
-LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger,
- SurfaceID id, const sp<LayerBuffer>& owner)
- : LayerBaseClient::Surface(flinger, id, owner->getIdentity(), owner)
+LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer(
+ const sp<SurfaceFlinger>& flinger, const sp<LayerBuffer>& owner)
+ : LayerBaseClient::Surface(flinger, owner->getIdentity(), owner)
{
}
diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h
index d38dde2..413b8a4 100644
--- a/libs/surfaceflinger/LayerBuffer.h
+++ b/libs/surfaceflinger/LayerBuffer.h
@@ -53,7 +53,7 @@
public:
LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i);
+ const sp<Client>& client);
virtual ~LayerBuffer();
virtual void onFirstRef();
@@ -189,7 +189,7 @@
{
public:
SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger,
- SurfaceID id, const sp<LayerBuffer>& owner);
+ const sp<LayerBuffer>& owner);
virtual ~SurfaceLayerBuffer();
virtual status_t registerBuffers(const ISurface::BufferHeap& buffers);
diff --git a/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp
index 568fedb..d528d2f 100644
--- a/libs/surfaceflinger/LayerDim.cpp
+++ b/libs/surfaceflinger/LayerDim.cpp
@@ -39,8 +39,8 @@
// ---------------------------------------------------------------------------
LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i)
- : LayerBaseClient(flinger, display, client, i)
+ const sp<Client>& client)
+ : LayerBaseClient(flinger, display, client)
{
}
diff --git a/libs/surfaceflinger/LayerDim.h b/libs/surfaceflinger/LayerDim.h
index 19a9990..f032314 100644
--- a/libs/surfaceflinger/LayerDim.h
+++ b/libs/surfaceflinger/LayerDim.h
@@ -38,7 +38,7 @@
static int32_t sHeight;
public:
LayerDim(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i);
+ const sp<Client>& client);
virtual ~LayerDim();
virtual void onDraw(const Region& clip) const;
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index fff0853..0f73774 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -227,54 +227,27 @@
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection()
{
- Mutex::Autolock _l(mStateLock);
- uint32_t token = mTokens.acquire();
-
- sp<Client> client = new Client(token, this);
- if (client->ctrlblk == 0) {
- mTokens.release(token);
- return 0;
+ sp<ISurfaceComposerClient> bclient;
+ sp<Client> client(new Client(this));
+ status_t err = client->initCheck();
+ if (err == NO_ERROR) {
+ bclient = client;
}
- status_t err = mClientsMap.add(token, client);
- if (err < 0) {
- mTokens.release(token);
- return 0;
- }
- sp<BClient> bclient =
- new BClient(this, token, client->getControlBlockMemory());
return bclient;
}
-void SurfaceFlinger::destroyConnection(ClientID cid)
+sp<ISurfaceComposerClient> SurfaceFlinger::createClientConnection()
{
- Mutex::Autolock _l(mStateLock);
- sp<Client> client = mClientsMap.valueFor(cid);
- if (client != 0) {
- // free all the layers this client owns
- Vector< wp<LayerBaseClient> > layers(client->getLayers());
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; i++) {
- sp<LayerBaseClient> layer(layers[i].promote());
- if (layer != 0) {
- purgatorizeLayer_l(layer);
- }
- }
-
- // the resources associated with this client will be freed
- // during the next transaction, after these surfaces have been
- // properly removed from the screen
-
- // remove this client from our ClientID->Client mapping.
- mClientsMap.removeItem(cid);
-
- // and add it to the list of disconnected clients
- mDisconnectedClients.add(client);
-
- // request a transaction
- setTransactionFlags(eTransactionNeeded);
+ sp<ISurfaceComposerClient> bclient;
+ sp<UserClient> client(new UserClient(this));
+ status_t err = client->initCheck();
+ if (err == NO_ERROR) {
+ bclient = client;
}
+ return bclient;
}
+
const GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) const
{
LOGE_IF(uint32_t(dpy) >= DISPLAY_COUNT, "Invalid DisplayID %d", dpy);
@@ -665,10 +638,6 @@
}
}
}
-
- // get rid of all resources we don't need anymore
- // (layers and clients)
- free_resources_l();
}
commitTransaction();
@@ -815,7 +784,8 @@
void SurfaceFlinger::handlePageFlip()
{
bool visibleRegions = mVisibleRegionsDirty;
- LayerVector& currentLayers = const_cast<LayerVector&>(mDrawingState.layersSortedByZ);
+ LayerVector& currentLayers = const_cast<LayerVector&>(
+ mDrawingState.layersSortedByZ);
visibleRegions |= lockPageFlip(currentLayers);
const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -837,7 +807,7 @@
size_t count = currentLayers.size();
sp<LayerBase> const* layers = currentLayers.array();
for (size_t i=0 ; i<count ; i++) {
- const sp<LayerBase>& layer = layers[i];
+ const sp<LayerBase>& layer(layers[i]);
layer->lockPageFlip(recomputeVisibleRegions);
}
return recomputeVisibleRegions;
@@ -850,7 +820,7 @@
size_t count = currentLayers.size();
sp<LayerBase> const* layers = currentLayers.array();
for (size_t i=0 ; i<count ; i++) {
- const sp<LayerBase>& layer = layers[i];
+ const sp<LayerBase>& layer(layers[i]);
layer->unlockPageFlip(planeTransform, mDirtyRegion);
}
}
@@ -882,7 +852,7 @@
// takes a rectangle, we must make sure to update that whole
// rectangle in that case
if (flags & DisplayHardware::SWAP_RECTANGLE) {
- // FIXME: we really should be able to pass a region to
+ // TODO: we really should be able to pass a region to
// SWAP_RECTANGLE so that we don't have to redraw all this.
mDirtyRegion.set(mInvalidRegion.bounds());
} else {
@@ -1071,6 +1041,27 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
+{
+ ssize_t i = mCurrentState.layersSortedByZ.add(
+ layer, &LayerBase::compareCurrentStateZ);
+ return (i < 0) ? status_t(i) : status_t(NO_ERROR);
+}
+
+ssize_t SurfaceFlinger::addClientLayer(const sp<Client>& client,
+ const sp<LayerBaseClient>& lbc)
+{
+ Mutex::Autolock _l(mStateLock);
+
+ // attach this layer to the client
+ ssize_t name = client->attachLayer(lbc);
+
+ // add this layer to the current state list
+ addLayer_l(lbc);
+
+ return name;
+}
+
status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer)
{
Mutex::Autolock _l(mStateLock);
@@ -1080,35 +1071,15 @@
return err;
}
-status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer)
-{
- layer->forceVisibilityTransaction();
- setTransactionFlags(eTraversalNeeded);
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
-{
- ssize_t i = mCurrentState.layersSortedByZ.add(
- layer, &LayerBase::compareCurrentStateZ);
- return (i < 0) ? status_t(i) : status_t(NO_ERROR);
-}
-
-status_t SurfaceFlinger::addClientLayer_l(const sp<LayerBaseClient>& lbc)
-{
- ssize_t serverIndex = lbc->serverIndex();
- return mLayerMap.add(serverIndex, lbc);
-}
-
status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase)
{
+ sp<LayerBaseClient> lbc(layerBase->getLayerBaseClient());
+ if (lbc != 0) {
+ mLayerMap.removeItem( lbc->getSurface()->asBinder() );
+ }
ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase);
if (index >= 0) {
mLayersRemoved = true;
- ssize_t serverIndex = layerBase->serverIndex();
- if (serverIndex >= 0) {
- mLayerMap.removeItem(serverIndex);
- }
return NO_ERROR;
}
return status_t(index);
@@ -1123,22 +1094,16 @@
// it's possible that we don't find a layer, because it might
// have been destroyed already -- this is not technically an error
- // from the user because there is a race between BClient::destroySurface(),
- // ~BClient() and ~ISurface().
+ // from the user because there is a race between Client::destroySurface(),
+ // ~Client() and ~ISurface().
return (err == NAME_NOT_FOUND) ? status_t(NO_ERROR) : err;
}
-
-void SurfaceFlinger::free_resources_l()
+status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer)
{
- // free resources associated with disconnected clients
- Vector< sp<Client> >& disconnectedClients(mDisconnectedClients);
- const size_t count = disconnectedClients.size();
- for (size_t i=0 ; i<count ; i++) {
- sp<Client> client = disconnectedClients[i];
- mTokens.release(client->cid);
- }
- disconnectedClients.clear();
+ layer->forceVisibilityTransaction();
+ setTransactionFlags(eTraversalNeeded);
+ return NO_ERROR;
}
uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags)
@@ -1229,7 +1194,7 @@
return orientation;
}
-sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,
+sp<ISurface> SurfaceFlinger::createSurface(const sp<Client>& client, int pid,
const String8& name, ISurfaceComposerClient::surface_data_t* params,
DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags)
@@ -1243,57 +1208,52 @@
return surfaceHandle;
}
- Mutex::Autolock _l(mStateLock);
- sp<Client> client = mClientsMap.valueFor(clientId);
- if (UNLIKELY(client == 0)) {
- LOGE("createSurface() failed, client not found (id=%d)", clientId);
- return surfaceHandle;
- }
-
//LOGD("createSurface for pid %d (%d x %d)", pid, w, h);
- int32_t id = client->generateId(pid);
- if (uint32_t(id) >= SharedBufferStack::NUM_LAYERS_MAX) {
- LOGE("createSurface() failed, generateId = %d", id);
- return surfaceHandle;
- }
-
+ sp<Layer> normalLayer;
switch (flags & eFXSurfaceMask) {
case eFXSurfaceNormal:
if (UNLIKELY(flags & ePushBuffers)) {
- layer = createPushBuffersSurfaceLocked(client, d, id,
- w, h, flags);
+ layer = createPushBuffersSurface(client, d, w, h, flags);
} else {
- layer = createNormalSurfaceLocked(client, d, id,
- w, h, flags, format);
+ normalLayer = createNormalSurface(client, d, w, h, flags, format);
+ layer = normalLayer;
}
break;
case eFXSurfaceBlur:
- layer = createBlurSurfaceLocked(client, d, id, w, h, flags);
+ layer = createBlurSurface(client, d, w, h, flags);
break;
case eFXSurfaceDim:
- layer = createDimSurfaceLocked(client, d, id, w, h, flags);
+ layer = createDimSurface(client, d, w, h, flags);
break;
}
if (layer != 0) {
+ layer->initStates(w, h, flags);
layer->setName(name);
- setTransactionFlags(eTransactionNeeded);
+ ssize_t token = addClientLayer(client, layer);
+
surfaceHandle = layer->getSurface();
if (surfaceHandle != 0) {
- params->token = surfaceHandle->getToken();
+ params->token = token;
params->identity = surfaceHandle->getIdentity();
params->width = w;
params->height = h;
params->format = format;
+ if (normalLayer != 0) {
+ Mutex::Autolock _l(mStateLock);
+ mLayerMap.add(surfaceHandle->asBinder(), normalLayer);
+ }
}
+
+ setTransactionFlags(eTransactionNeeded);
}
return surfaceHandle;
}
-sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked(
+sp<Layer> SurfaceFlinger::createNormalSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+ uint32_t w, uint32_t h, uint32_t flags,
PixelFormat& format)
{
// initialize the surfaces
@@ -1307,53 +1267,43 @@
break;
}
- sp<Layer> layer = new Layer(this, display, client, id);
+ sp<Layer> layer = new Layer(this, display, client);
status_t err = layer->setBuffers(w, h, format, flags);
- if (LIKELY(err == NO_ERROR)) {
- layer->initStates(w, h, flags);
- addLayer_l(layer);
- addClientLayer_l(layer);
- } else {
+ if (LIKELY(err != NO_ERROR)) {
LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err));
layer.clear();
}
return layer;
}
-sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked(
+sp<LayerBlur> SurfaceFlinger::createBlurSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+ uint32_t w, uint32_t h, uint32_t flags)
{
- sp<LayerBlur> layer = new LayerBlur(this, display, client, id);
+ sp<LayerBlur> layer = new LayerBlur(this, display, client);
layer->initStates(w, h, flags);
- addLayer_l(layer);
- addClientLayer_l(layer);
return layer;
}
-sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked(
+sp<LayerDim> SurfaceFlinger::createDimSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+ uint32_t w, uint32_t h, uint32_t flags)
{
- sp<LayerDim> layer = new LayerDim(this, display, client, id);
+ sp<LayerDim> layer = new LayerDim(this, display, client);
layer->initStates(w, h, flags);
- addLayer_l(layer);
- addClientLayer_l(layer);
return layer;
}
-sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked(
+sp<LayerBuffer> SurfaceFlinger::createPushBuffersSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+ uint32_t w, uint32_t h, uint32_t flags)
{
- sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id);
+ sp<LayerBuffer> layer = new LayerBuffer(this, display, client);
layer->initStates(w, h, flags);
- addLayer_l(layer);
- addClientLayer_l(layer);
return layer;
}
-status_t SurfaceFlinger::removeSurface(SurfaceID index)
+status_t SurfaceFlinger::removeSurface(const sp<Client>& client, SurfaceID sid)
{
/*
* called by the window manager, when a surface should be marked for
@@ -1366,7 +1316,7 @@
status_t err = NAME_NOT_FOUND;
Mutex::Autolock _l(mStateLock);
- sp<LayerBaseClient> layer = getLayerUser_l(index);
+ sp<LayerBaseClient> layer = client->getLayerUser(sid);
if (layer != 0) {
err = purgatorizeLayer_l(layer);
if (err == NO_ERROR) {
@@ -1411,16 +1361,15 @@
}
status_t SurfaceFlinger::setClientState(
- ClientID cid,
+ const sp<Client>& client,
int32_t count,
const layer_state_t* states)
{
Mutex::Autolock _l(mStateLock);
uint32_t flags = 0;
- cid <<= 16;
for (int i=0 ; i<count ; i++) {
- const layer_state_t& s = states[i];
- sp<LayerBaseClient> layer(getLayerUser_l(s.surface | cid));
+ const layer_state_t& s(states[i]);
+ sp<LayerBaseClient> layer(client->getLayerUser(s.surface));
if (layer != 0) {
const uint32_t what = s.what;
if (what & ePositionChanged) {
@@ -1466,12 +1415,6 @@
return NO_ERROR;
}
-sp<LayerBaseClient> SurfaceFlinger::getLayerUser_l(SurfaceID s) const
-{
- sp<LayerBaseClient> layer = mLayerMap.valueFor(s);
- return layer;
-}
-
void SurfaceFlinger::screenReleased(int dpy)
{
// this may be called by a signal handler, we can't do too much in here
@@ -1557,8 +1500,6 @@
result.append(buffer);
}
- snprintf(buffer, SIZE, " client count: %d\n", mClientsMap.size());
- result.append(buffer);
const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
alloc.dump(result);
@@ -1651,12 +1592,91 @@
}
// ---------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger)
- : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger)
+sp<Layer> SurfaceFlinger::getLayer(const sp<ISurface>& sur) const
+{
+ sp<Layer> result;
+ Mutex::Autolock _l(mStateLock);
+ result = mLayerMap.valueFor( sur->asBinder() ).promote();
+ return result;
+}
+
+// ---------------------------------------------------------------------------
+
+Client::Client(const sp<SurfaceFlinger>& flinger)
+ : mFlinger(flinger), mNameGenerator(1)
+{
+}
+
+Client::~Client()
+{
+ const size_t count = mLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ sp<LayerBaseClient> layer(mLayers.valueAt(i).promote());
+ if (layer != 0) {
+ mFlinger->removeLayer(layer);
+ }
+ }
+}
+
+status_t Client::initCheck() const {
+ return NO_ERROR;
+}
+
+ssize_t Client::attachLayer(const sp<LayerBaseClient>& layer)
+{
+ int32_t name = android_atomic_inc(&mNameGenerator);
+ mLayers.add(name, layer);
+ return name;
+}
+
+void Client::detachLayer(const LayerBaseClient* layer)
+{
+ // we do a linear search here, because this doesn't happen often
+ const size_t count = mLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ if (mLayers.valueAt(i) == layer) {
+ mLayers.removeItemsAt(i, 1);
+ break;
+ }
+ }
+}
+sp<LayerBaseClient> Client::getLayerUser(int32_t i) const {
+ sp<LayerBaseClient> lbc;
+ const wp<LayerBaseClient>& layer(mLayers.valueFor(i));
+ if (layer != 0) {
+ lbc = layer.promote();
+ LOGE_IF(lbc==0, "getLayerUser(name=%d) is dead", int(i));
+ }
+ return lbc;
+}
+
+sp<IMemoryHeap> Client::getControlBlock() const {
+ return 0;
+}
+ssize_t Client::getTokenForSurface(const sp<ISurface>& sur) const {
+ return -1;
+}
+sp<ISurface> Client::createSurface(
+ ISurfaceComposerClient::surface_data_t* params, int pid,
+ const String8& name,
+ DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags)
+{
+ return mFlinger->createSurface(this, pid, name, params,
+ display, w, h, format, flags);
+}
+status_t Client::destroySurface(SurfaceID sid) {
+ return mFlinger->removeSurface(this, sid);
+}
+status_t Client::setState(int32_t count, const layer_state_t* states) {
+ return mFlinger->setClientState(this, count, states);
+}
+
+// ---------------------------------------------------------------------------
+
+UserClient::UserClient(const sp<SurfaceFlinger>& flinger)
+ : ctrlblk(0), mBitmap(0), mFlinger(flinger)
{
const int pgsize = getpagesize();
const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));
@@ -1670,94 +1690,81 @@
}
}
-Client::~Client() {
+UserClient::~UserClient()
+{
if (ctrlblk) {
ctrlblk->~SharedClient(); // destroy our shared-structure.
}
+
+ /*
+ * When a UserClient dies, it's unclear what to do exactly.
+ * We could go ahead and destroy all surfaces linked to that client
+ * however, it wouldn't be fair to the main Client
+ * (usually the the window-manager), which might want to re-target
+ * the layer to another UserClient.
+ * I think the best is to do nothing, or not much; in most cases the
+ * WM itself will go ahead and clean things up when it detects a client of
+ * his has died.
+ * The remaining question is what to display? currently we keep
+ * just keep the current buffer.
+ */
}
-int32_t Client::generateId(int pid)
-{
- const uint32_t i = clz( ~mBitmap );
- if (i >= SharedBufferStack::NUM_LAYERS_MAX) {
- return NO_MEMORY;
- }
- mPid = pid;
- mInUse.add(uint8_t(i));
- mBitmap |= 1<<(31-i);
- return i;
+status_t UserClient::initCheck() const {
+ return ctrlblk == 0 ? NO_INIT : NO_ERROR;
}
-status_t Client::bindLayer(const sp<LayerBaseClient>& layer, int32_t id)
+void UserClient::detachLayer(const Layer* layer)
{
- ssize_t idx = mInUse.indexOf(id);
- if (idx < 0)
- return NAME_NOT_FOUND;
- return mLayers.insertAt(layer, idx);
-}
-
-void Client::free(int32_t id)
-{
- ssize_t idx = mInUse.remove(uint8_t(id));
- if (idx >= 0) {
- mBitmap &= ~(1<<(31-id));
- mLayers.removeItemsAt(idx);
+ int32_t name = layer->getToken();
+ if (name >= 0) {
+ android_atomic_and(~(1LU<<name), &mBitmap);
}
}
-bool Client::isValid(int32_t i) const {
- return (uint32_t(i)<SharedBufferStack::NUM_LAYERS_MAX) &&
- (mBitmap & (1<<(31-i)));
+sp<IMemoryHeap> UserClient::getControlBlock() const {
+ return mCblkHeap;
}
-sp<LayerBaseClient> Client::getLayerUser(int32_t i) const {
- sp<LayerBaseClient> lbc;
- ssize_t idx = mInUse.indexOf(uint8_t(i));
- if (idx >= 0) {
- lbc = mLayers[idx].promote();
- LOGE_IF(lbc==0, "getLayerUser(i=%d), idx=%d is dead", int(i), int(idx));
- }
- return lbc;
-}
-
-// ---------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
-BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk)
- : mId(cid), mFlinger(flinger), mCblk(cblk)
+ssize_t UserClient::getTokenForSurface(const sp<ISurface>& sur) const
{
+ int32_t name = NAME_NOT_FOUND;
+ sp<Layer> layer(mFlinger->getLayer(sur));
+ if (layer == 0) return name;
+
+ // this layer already has a token, just return it
+ // FIXME: we should check that this token is for the same client
+ name = layer->getToken();
+ if (name >= 0) return name;
+
+ name = 0;
+ do {
+ int32_t mask = 1LU<<name;
+ if ((android_atomic_or(mask, &mBitmap) & mask) == 0) {
+ // we found and locked that name
+ layer->setToken(const_cast<UserClient*>(this), ctrlblk, name);
+ break;
+ }
+ if (++name > 31)
+ name = NO_MEMORY;
+ } while(name >= 0);
+
+ //LOGD("getTokenForSurface(%p) => %d", sur->asBinder().get(), name);
+ return name;
}
-BClient::~BClient() {
- // destroy all resources attached to this client
- mFlinger->destroyConnection(mId);
-}
-
-sp<IMemoryHeap> BClient::getControlBlock() const {
- return mCblk;
-}
-
-sp<ISurface> BClient::createSurface(
+sp<ISurface> UserClient::createSurface(
ISurfaceComposerClient::surface_data_t* params, int pid,
const String8& name,
DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
- uint32_t flags)
-{
- return mFlinger->createSurface(mId, pid, name, params, display, w, h,
- format, flags);
+ uint32_t flags) {
+ return 0;
}
-
-status_t BClient::destroySurface(SurfaceID sid)
-{
- sid |= (mId << 16); // add the client-part to id
- return mFlinger->removeSurface(sid);
+status_t UserClient::destroySurface(SurfaceID sid) {
+ return INVALID_OPERATION;
}
-
-status_t BClient::setState(int32_t count, const layer_state_t* states)
-{
- return mFlinger->setClientState(mId, count, states);
+status_t UserClient::setState(int32_t count, const layer_state_t* states) {
+ return INVALID_OPERATION;
}
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h
index d8fe98c..0bfc170 100644
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ b/libs/surfaceflinger/SurfaceFlinger.h
@@ -36,7 +36,6 @@
#include "Barrier.h"
#include "Layer.h"
-#include "Tokenizer.h"
#include "MessageQueue.h"
@@ -48,54 +47,80 @@
// ---------------------------------------------------------------------------
class Client;
-class BClient;
class DisplayHardware;
class FreezeLock;
class Layer;
+class LayerBlur;
+class LayerDim;
class LayerBuffer;
-typedef int32_t ClientID;
-
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
// ---------------------------------------------------------------------------
-class Client : public RefBase
+class Client : public BnSurfaceComposerClient
{
public:
- Client(ClientID cid, const sp<SurfaceFlinger>& flinger);
- ~Client();
+ Client(const sp<SurfaceFlinger>& flinger);
+ ~Client();
- int32_t generateId(int pid);
- void free(int32_t id);
- status_t bindLayer(const sp<LayerBaseClient>& layer, int32_t id);
+ status_t initCheck() const;
- inline bool isValid(int32_t i) const;
- sp<LayerBaseClient> getLayerUser(int32_t i) const;
-
- const Vector< wp<LayerBaseClient> >& getLayers() const {
- return mLayers;
- }
-
- const sp<IMemoryHeap>& getControlBlockMemory() const {
- return mCblkHeap;
- }
-
- // pointer to this client's control block
- SharedClient* ctrlblk;
- ClientID cid;
+ // protected by SurfaceFlinger::mStateLock
+ ssize_t attachLayer(const sp<LayerBaseClient>& layer);
+ void detachLayer(const LayerBaseClient* layer);
+ sp<LayerBaseClient> getLayerUser(int32_t i) const;
-
private:
- int getClientPid() const { return mPid; }
-
- int mPid;
- uint32_t mBitmap;
- SortedVector<uint8_t> mInUse;
- Vector< wp<LayerBaseClient> > mLayers;
- sp<IMemoryHeap> mCblkHeap;
- sp<SurfaceFlinger> mFlinger;
+
+ // ISurfaceComposerClient interface
+ virtual sp<IMemoryHeap> getControlBlock() const;
+ virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const;
+ virtual sp<ISurface> createSurface(
+ surface_data_t* params, int pid, const String8& name,
+ DisplayID display, uint32_t w, uint32_t h,PixelFormat format,
+ uint32_t flags);
+ virtual status_t destroySurface(SurfaceID surfaceId);
+ virtual status_t setState(int32_t count, const layer_state_t* states);
+
+ DefaultKeyedVector< size_t, wp<LayerBaseClient> > mLayers;
+ sp<SurfaceFlinger> mFlinger;
+ int32_t mNameGenerator;
+};
+
+class UserClient : public BnSurfaceComposerClient
+{
+public:
+ // pointer to this client's control block
+ SharedClient* ctrlblk;
+
+public:
+ UserClient(const sp<SurfaceFlinger>& flinger);
+ ~UserClient();
+
+ status_t initCheck() const;
+
+ // protected by SurfaceFlinger::mStateLock
+ void detachLayer(const Layer* layer);
+
+private:
+
+ // ISurfaceComposerClient interface
+ virtual sp<IMemoryHeap> getControlBlock() const;
+ virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const;
+ virtual sp<ISurface> createSurface(
+ surface_data_t* params, int pid, const String8& name,
+ DisplayID display, uint32_t w, uint32_t h,PixelFormat format,
+ uint32_t flags);
+ virtual status_t destroySurface(SurfaceID surfaceId);
+ virtual status_t setState(int32_t count, const layer_state_t* states);
+
+ // atomic-ops
+ mutable volatile int32_t mBitmap;
+
+ sp<IMemoryHeap> mCblkHeap;
+ sp<SurfaceFlinger> mFlinger;
};
// ---------------------------------------------------------------------------
@@ -159,6 +184,7 @@
// ISurfaceComposer interface
virtual sp<ISurfaceComposerClient> createConnection();
+ virtual sp<ISurfaceComposerClient> createClientConnection();
virtual sp<IMemoryHeap> getCblk() const;
virtual void bootFinished();
virtual void openGlobalTransaction();
@@ -173,13 +199,14 @@
overlay_control_device_t* getOverlayEngine() const;
-
status_t removeLayer(const sp<LayerBase>& layer);
status_t addLayer(const sp<LayerBase>& layer);
status_t invalidateLayerVisibility(const sp<LayerBase>& layer);
-
+
+ sp<Layer> getLayer(const sp<ISurface>& sur) const;
+
private:
- friend class BClient;
+ friend class Client;
friend class LayerBase;
friend class LayerBuffer;
friend class LayerBaseClient;
@@ -188,31 +215,33 @@
friend class LayerBlur;
friend class LayerDim;
- sp<ISurface> createSurface(ClientID client, int pid, const String8& name,
+ sp<ISurface> createSurface(const sp<Client>& client,
+ int pid, const String8& name,
ISurfaceComposerClient::surface_data_t* params,
DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags);
- sp<LayerBaseClient> createNormalSurfaceLocked(
+ sp<Layer> createNormalSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+ uint32_t w, uint32_t h, uint32_t flags,
PixelFormat& format);
- sp<LayerBaseClient> createBlurSurfaceLocked(
+ sp<LayerBlur> createBlurSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+ uint32_t w, uint32_t h, uint32_t flags);
- sp<LayerBaseClient> createDimSurfaceLocked(
+ sp<LayerDim> createDimSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+ uint32_t w, uint32_t h, uint32_t flags);
- sp<LayerBaseClient> createPushBuffersSurfaceLocked(
+ sp<LayerBuffer> createPushBuffersSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+ uint32_t w, uint32_t h, uint32_t flags);
- status_t removeSurface(SurfaceID surface_id);
+ status_t removeSurface(const sp<Client>& client, SurfaceID sid);
status_t destroySurface(const sp<LayerBaseClient>& layer);
- status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states);
+ status_t setClientState(const sp<Client>& client,
+ int32_t count, const layer_state_t* states);
class LayerVector {
@@ -275,13 +304,11 @@
void unlockClients();
- void destroyConnection(ClientID cid);
- sp<LayerBaseClient> getLayerUser_l(SurfaceID index) const;
+ ssize_t addClientLayer(const sp<Client>& client,
+ const sp<LayerBaseClient>& lbc);
status_t addLayer_l(const sp<LayerBase>& layer);
- status_t addClientLayer_l(const sp<LayerBaseClient>& lbc);
status_t removeLayer_l(const sp<LayerBase>& layer);
status_t purgatorizeLayer_l(const sp<LayerBase>& layer);
- void free_resources_l();
uint32_t getTransactionFlags(uint32_t flags);
uint32_t setTransactionFlags(uint32_t flags);
@@ -314,8 +341,7 @@
status_t postMessageSync(const sp<MessageBase>& msg,
nsecs_t reltime=0, uint32_t flags = 0);
-
-
+
// access must be protected by mStateLock
mutable Mutex mStateLock;
State mCurrentState;
@@ -324,14 +350,11 @@
volatile int32_t mTransactionCount;
Condition mTransactionCV;
bool mResizeTransationPending;
-
+
// protected by mStateLock (but we could use another lock)
- Tokenizer mTokens;
- DefaultKeyedVector<ClientID, sp<Client> > mClientsMap;
- DefaultKeyedVector<SurfaceID, sp<LayerBaseClient> > mLayerMap;
- GraphicPlane mGraphicPlanes[1];
- bool mLayersRemoved;
- Vector< sp<Client> > mDisconnectedClients;
+ GraphicPlane mGraphicPlanes[1];
+ bool mLayersRemoved;
+ DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayerMap;
// constant members (no synchronization needed for access)
sp<IMemoryHeap> mServerHeap;
@@ -392,32 +415,6 @@
};
// ---------------------------------------------------------------------------
-
-class BClient : public BnSurfaceComposerClient
-{
-public:
- BClient(SurfaceFlinger *flinger, ClientID cid,
- const sp<IMemoryHeap>& cblk);
- ~BClient();
-
- // ISurfaceComposerClient interface
- virtual sp<IMemoryHeap> getControlBlock() const;
-
- virtual sp<ISurface> createSurface(
- surface_data_t* params, int pid, const String8& name,
- DisplayID display, uint32_t w, uint32_t h,PixelFormat format,
- uint32_t flags);
-
- virtual status_t destroySurface(SurfaceID surfaceId);
- virtual status_t setState(int32_t count, const layer_state_t* states);
-
-private:
- ClientID mId;
- SurfaceFlinger* mFlinger;
- sp<IMemoryHeap> mCblk;
-};
-
-// ---------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_SURFACE_FLINGER_H
diff --git a/libs/surfaceflinger/Tokenizer.cpp b/libs/surfaceflinger/Tokenizer.cpp
deleted file mode 100644
index be3a239..0000000
--- a/libs/surfaceflinger/Tokenizer.cpp
+++ /dev/null
@@ -1,173 +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.
- */
-
-#include <stdio.h>
-
-#include "Tokenizer.h"
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t)
-
-Tokenizer::Tokenizer()
-{
-}
-
-Tokenizer::Tokenizer(const Tokenizer& other)
- : mRanges(other.mRanges)
-{
-}
-
-Tokenizer::~Tokenizer()
-{
-}
-
-uint32_t Tokenizer::acquire()
-{
- if (!mRanges.size() || mRanges[0].first) {
- _insertTokenAt(0,0);
- return 0;
- }
-
- // just extend the first run
- const run_t& run = mRanges[0];
- uint32_t token = run.first + run.length;
- _insertTokenAt(token, 1);
- return token;
-}
-
-bool Tokenizer::isAcquired(uint32_t token) const
-{
- return (_indexOrderOf(token) >= 0);
-}
-
-status_t Tokenizer::reserve(uint32_t token)
-{
- size_t o;
- const ssize_t i = _indexOrderOf(token, &o);
- if (i >= 0) {
- return BAD_VALUE; // this token is already taken
- }
- ssize_t err = _insertTokenAt(token, o);
- return (err<0) ? err : status_t(NO_ERROR);
-}
-
-status_t Tokenizer::release(uint32_t token)
-{
- const ssize_t i = _indexOrderOf(token);
- if (i >= 0) {
- const run_t& run = mRanges[i];
- if ((token >= run.first) && (token < run.first+run.length)) {
- // token in this range, we need to split
- run_t& run = mRanges.editItemAt(i);
- if ((token == run.first) || (token == run.first+run.length-1)) {
- if (token == run.first) {
- run.first += 1;
- }
- run.length -= 1;
- if (run.length == 0) {
- // XXX: should we systematically remove a run that's empty?
- mRanges.removeItemsAt(i);
- }
- } else {
- // split the run
- run_t new_run;
- new_run.first = token+1;
- new_run.length = run.first+run.length - new_run.first;
- run.length = token - run.first;
- mRanges.insertAt(new_run, i+1);
- }
- return NO_ERROR;
- }
- }
- return NAME_NOT_FOUND;
-}
-
-ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const
-{
- // binary search
- ssize_t err = NAME_NOT_FOUND;
- ssize_t l = 0;
- ssize_t h = mRanges.size()-1;
- ssize_t mid;
- const run_t* a = mRanges.array();
- while (l <= h) {
- mid = l + (h - l)/2;
- const run_t* const curr = a + mid;
- int c = 0;
- if (token < curr->first) c = 1;
- else if (token >= curr->first+curr->length) c = -1;
- if (c == 0) {
- err = l = mid;
- break;
- } else if (c < 0) {
- l = mid + 1;
- } else {
- h = mid - 1;
- }
- }
- if (order) *order = l;
- return err;
-}
-
-ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index)
-{
- const size_t c = mRanges.size();
-
- if (index >= 1) {
- // do we need to merge with the previous run?
- run_t& p = mRanges.editItemAt(index-1);
- if (p.first+p.length == token) {
- p.length += 1;
- if (index < c) {
- const run_t& n = mRanges[index];
- if (token+1 == n.first) {
- p.length += n.length;
- mRanges.removeItemsAt(index);
- }
- }
- return index;
- }
- }
-
- if (index < c) {
- // do we need to merge with the next run?
- run_t& n = mRanges.editItemAt(index);
- if (token+1 == n.first) {
- n.first -= 1;
- n.length += 1;
- return index;
- }
- }
-
- return mRanges.insertAt(run_t(token,1), index);
-}
-
-void Tokenizer::dump() const
-{
- const run_t* ranges = mRanges.array();
- const size_t c = mRanges.size();
- printf("Tokenizer (%p, size = %d)\n", this, int(c));
- for (size_t i=0 ; i<c ; i++) {
- printf("%u: (%u, %u)\n", i,
- uint32_t(ranges[i].first), uint32_t(ranges[i].length));
- }
-}
-
-}; // namespace android
-
diff --git a/libs/surfaceflinger/Tokenizer.h b/libs/surfaceflinger/Tokenizer.h
deleted file mode 100644
index 6b3057d..0000000
--- a/libs/surfaceflinger/Tokenizer.h
+++ /dev/null
@@ -1,57 +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.
- */
-
-#ifndef ANDROID_TOKENIZER_H
-#define ANDROID_TOKENIZER_H
-
-#include <utils/Vector.h>
-#include <utils/Errors.h>
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-class Tokenizer
-{
-public:
- Tokenizer();
- Tokenizer(const Tokenizer& other);
- ~Tokenizer();
-
- uint32_t acquire();
- status_t reserve(uint32_t token);
- status_t release(uint32_t token);
- bool isAcquired(uint32_t token) const;
-
- void dump() const;
-
- struct run_t {
- run_t() {};
- run_t(uint32_t f, uint32_t l) : first(f), length(l) {}
- uint32_t first;
- uint32_t length;
- };
-private:
- ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const;
- ssize_t _insertTokenAt(uint32_t token, size_t index);
- Vector<run_t> mRanges;
-};
-
-}; // namespace android
-
-// ----------------------------------------------------------------------------
-
-#endif // ANDROID_TOKENIZER_H
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index 50495c1..5c111f6 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -55,6 +55,15 @@
return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
+ virtual sp<ISurfaceComposerClient> createClientConnection()
+ {
+ uint32_t n;
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ remote()->transact(BnSurfaceComposer::CREATE_CLIENT_CONNECTION, data, &reply);
+ return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
+ }
+
virtual sp<IMemoryHeap> getCblk() const
{
Parcel data, reply;
@@ -136,6 +145,11 @@
sp<IBinder> b = createConnection()->asBinder();
reply->writeStrongBinder(b);
} break;
+ case CREATE_CLIENT_CONNECTION: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> b = createClientConnection()->asBinder();
+ reply->writeStrongBinder(b);
+ } break;
case OPEN_GLOBAL_TRANSACTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
openGlobalTransaction();
diff --git a/libs/surfaceflinger_client/ISurfaceComposerClient.cpp b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp
index 67c7df81..2cc1f8e 100644
--- a/libs/surfaceflinger_client/ISurfaceComposerClient.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp
@@ -51,6 +51,7 @@
enum {
GET_CBLK = IBinder::FIRST_CALL_TRANSACTION,
+ GET_TOKEN,
CREATE_SURFACE,
DESTROY_SURFACE,
SET_STATE
@@ -72,6 +73,15 @@
return interface_cast<IMemoryHeap>(reply.readStrongBinder());
}
+ virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
+ data.writeStrongBinder(sur->asBinder());
+ remote()->transact(GET_TOKEN, data, &reply);
+ return reply.readInt32();
+ }
+
virtual sp<ISurface> createSurface( surface_data_t* params,
int pid,
const String8& name,
@@ -132,6 +142,13 @@
reply->writeStrongBinder(ctl->asBinder());
return NO_ERROR;
} break;
+ case GET_TOKEN: {
+ CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
+ sp<ISurface> sur = interface_cast<ISurface>(data.readStrongBinder());
+ ssize_t token = getTokenForSurface(sur);
+ reply->writeInt32(token);
+ return NO_ERROR;
+ } break;
}
// these must be checked
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 8d03145..1dd8642 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -58,7 +58,7 @@
void SharedBufferStack::init(int32_t i)
{
- inUse = -1;
+ inUse = -2;
status = NO_ERROR;
identity = i;
}
@@ -282,8 +282,10 @@
}
ssize_t SharedBufferServer::UnlockUpdate::operator()() {
if (stack.inUse != lockedBuffer) {
- LOGE("unlocking %d, but currently locked buffer is %d",
- lockedBuffer, stack.inUse);
+ LOGE("unlocking %d, but currently locked buffer is %d "
+ "(identity=%d, token=%d)",
+ lockedBuffer, stack.inUse,
+ stack.identity, stack.token);
return BAD_VALUE;
}
android_atomic_write(-1, &stack.inUse);
@@ -480,6 +482,7 @@
mNumBuffers(num)
{
mSharedStack->init(identity);
+ mSharedStack->token = surface;
mSharedStack->head = num-1;
mSharedStack->available = num;
mSharedStack->queued = 0;
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index ac4b198..6fe4c4a 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -238,14 +238,12 @@
{
uint32_t flags = 0;
uint32_t format = 0;
- SurfaceID token = -1;
uint32_t identity = 0;
uint32_t width = 0;
uint32_t height = 0;
sp<SurfaceComposerClient> client;
sp<ISurface> sur;
if (SurfaceControl::isValid(control)) {
- token = control->mToken;
identity = control->mIdentity;
client = control->mClient;
sur = control->mSurface;
@@ -254,9 +252,7 @@
format = control->mFormat;
flags = control->mFlags;
}
- parcel->writeStrongBinder(client!=0 ? client->connection() : NULL);
- parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
- parcel->writeInt32(token);
+ parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
parcel->writeInt32(identity);
parcel->writeInt32(width);
parcel->writeInt32(height);
@@ -278,34 +274,92 @@
// Surface
// ============================================================================
+class SurfaceClient : public Singleton<SurfaceClient>
+{
+ // all these attributes are constants
+ sp<ISurfaceComposer> mComposerService;
+ sp<ISurfaceComposerClient> mClient;
+ status_t mStatus;
+ SharedClient* mControl;
+ sp<IMemoryHeap> mControlMemory;
+
+ SurfaceClient()
+ : Singleton<SurfaceClient>(), mStatus(NO_INIT)
+ {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ mComposerService = sf;
+ mClient = sf->createClientConnection();
+ if (mClient != NULL) {
+ mControlMemory = mClient->getControlBlock();
+ if (mControlMemory != NULL) {
+ mControl = static_cast<SharedClient *>(
+ mControlMemory->getBase());
+ if (mControl) {
+ mStatus = NO_ERROR;
+ }
+ }
+ }
+ }
+ friend class Singleton<SurfaceClient>;
+public:
+ status_t initCheck() const {
+ return mStatus;
+ }
+ SharedClient* getSharedClient() const {
+ return mControl;
+ }
+ ssize_t getTokenForSurface(const sp<ISurface>& sur) const {
+ // TODO: we could cache a few tokens here to avoid an IPC
+ return mClient->getTokenForSurface(sur);
+ }
+ void signalServer() const {
+ mComposerService->signal();
+ }
+};
+
+ANDROID_SINGLETON_STATIC_INSTANCE(SurfaceClient);
+
+// ---------------------------------------------------------------------------
+
Surface::Surface(const sp<SurfaceControl>& surface)
- : mSurface(surface->mSurface),
- mToken(surface->mToken), mIdentity(surface->mIdentity),
- mFormat(surface->mFormat), mFlags(surface->mFlags),
- mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL),
+ : mBufferMapper(GraphicBufferMapper::get()),
+ mClient(SurfaceClient::getInstance()),
+ mSharedBufferClient(NULL),
mInitCheck(NO_INIT),
+ mSurface(surface->mSurface),
+ mIdentity(surface->mIdentity),
+ mFormat(surface->mFormat), mFlags(surface->mFlags),
mWidth(surface->mWidth), mHeight(surface->mHeight)
{
- mClient = new SurfaceClient(surface->mClient);
init();
}
-Surface::Surface(const Parcel& parcel)
- : mBufferMapper(GraphicBufferMapper::get()),
- mSharedBufferClient(NULL), mInitCheck(NO_INIT)
+Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
+ : mBufferMapper(GraphicBufferMapper::get()),
+ mClient(SurfaceClient::getInstance()),
+ mSharedBufferClient(NULL),
+ mInitCheck(NO_INIT)
{
- sp<IBinder> conn = parcel.readStrongBinder();
- mSurface = interface_cast<ISurface>(parcel.readStrongBinder());
- mToken = parcel.readInt32();
+ mSurface = interface_cast<ISurface>(ref);
mIdentity = parcel.readInt32();
mWidth = parcel.readInt32();
mHeight = parcel.readInt32();
mFormat = parcel.readInt32();
mFlags = parcel.readInt32();
- mClient = new SurfaceClient(conn);
init();
}
+sp<Surface> Surface::readFromParcel(
+ const Parcel& data, const sp<Surface>& other)
+{
+ sp<Surface> result(other);
+ sp<IBinder> binder(data.readStrongBinder());
+ if (other==0 || binder != other->mSurface->asBinder()) {
+ result = new Surface(data, binder);
+ }
+ return result;
+}
+
void Surface::init()
{
android_native_window_t::setSwapInterval = setSwapInterval;
@@ -330,12 +384,14 @@
mBuffers.setCapacity(2);
mBuffers.insertAt(0, 2);
- if (mClient != 0 && mClient->initCheck() == NO_ERROR) {
- mSharedBufferClient = new SharedBufferClient(
- mClient->getSharedClient(), mToken, 2, mIdentity);
+ if (mSurface != 0 && mClient.initCheck() == NO_ERROR) {
+ mToken = mClient.getTokenForSurface(mSurface);
+ if (mToken >= 0) {
+ mSharedBufferClient = new SharedBufferClient(
+ mClient.getSharedClient(), mToken, 2, mIdentity);
+ mInitCheck = mClient.getSharedClient()->validate(mToken);
+ }
}
-
- mInitCheck = initCheck();
}
Surface::~Surface()
@@ -352,25 +408,11 @@
// clear all references and trigger an IPC now, to make sure things
// happen without delay, since these resources are quite heavy.
mBuffers.clear();
- mClient.clear();
mSurface.clear();
delete mSharedBufferClient;
IPCThreadState::self()->flushCommands();
}
-status_t Surface::initCheck() const
-{
- if (mToken<0 || mClient==0 || mClient->initCheck() != NO_ERROR) {
- return NO_INIT;
- }
- SharedClient const* cblk = mClient->getSharedClient();
- if (cblk == 0) {
- LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity);
- return NO_INIT;
- }
- return cblk->validate(mToken);
-}
-
bool Surface::isValid() {
return mInitCheck == NO_ERROR;
}
@@ -379,8 +421,7 @@
{
// check that we initialized ourself properly
if (mInitCheck != NO_ERROR) {
- LOGE("invalid token (%d, identity=%u) or client (%p)",
- mToken, mIdentity, mClient.get());
+ LOGE("invalid token (%d, identity=%u)", mToken, mIdentity);
return mInitCheck;
}
@@ -413,12 +454,6 @@
return NO_ERROR;
}
-bool Surface::isSameSurface(const sp<Surface>& lhs, const sp<Surface>& rhs) {
- if (lhs == 0 || rhs == 0)
- return false;
- return lhs->mSurface->asBinder() == rhs->mSurface->asBinder();
-}
-
sp<ISurface> Surface::getISurface() const {
return mSurface;
}
@@ -558,8 +593,8 @@
LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err));
if (err == NO_ERROR) {
- // FIXME: can we avoid this IPC if we know there is one pending?
- mClient->signalServer();
+ // TODO: can we avoid this IPC if we know there is one pending?
+ mClient.signalServer();
}
return err;
}
diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
index 0670d20..5ac0d5d 100644
--- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp
+++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
@@ -42,36 +42,26 @@
namespace android {
// ---------------------------------------------------------------------------
-class ComposerService : public Singleton<ComposerService>
-{
- // these are constants
- sp<ISurfaceComposer> mComposerService;
- sp<IMemoryHeap> mServerCblkMemory;
- surface_flinger_cblk_t volatile* mServerCblk;
-
- ComposerService() : Singleton<ComposerService>() {
- const String16 name("SurfaceFlinger");
- while (getService(name, &mComposerService) != NO_ERROR) {
- usleep(250000);
- }
- mServerCblkMemory = mComposerService->getCblk();
- mServerCblk = static_cast<surface_flinger_cblk_t volatile *>(
- mServerCblkMemory->getBase());
- }
-
- friend class Singleton<ComposerService>;
-
-public:
- static sp<ISurfaceComposer> getComposerService() {
- return ComposerService::getInstance().mComposerService;
- }
- static surface_flinger_cblk_t const volatile * getControlBlock() {
- return ComposerService::getInstance().mServerCblk;
- }
-};
-
ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService);
+ComposerService::ComposerService()
+: Singleton<ComposerService>() {
+ const String16 name("SurfaceFlinger");
+ while (getService(name, &mComposerService) != NO_ERROR) {
+ usleep(250000);
+ }
+ mServerCblkMemory = mComposerService->getCblk();
+ mServerCblk = static_cast<surface_flinger_cblk_t volatile *>(
+ mServerCblkMemory->getBase());
+}
+
+sp<ISurfaceComposer> ComposerService::getComposerService() {
+ return ComposerService::getInstance().mComposerService;
+}
+
+surface_flinger_cblk_t const volatile * ComposerService::getControlBlock() {
+ return ComposerService::getInstance().mServerCblk;
+}
static inline sp<ISurfaceComposer> getComposerService() {
return ComposerService::getComposerService();
@@ -557,41 +547,5 @@
}
// ----------------------------------------------------------------------------
-
-SurfaceClient::SurfaceClient(const sp<SurfaceComposerClient>& client)
- : mStatus(NO_INIT), mControl(0)
-{
- if (client != 0) {
- sp<IBinder> conn = client->connection();
- init(conn);
- }
-}
-SurfaceClient::SurfaceClient(const sp<IBinder>& conn)
- : mStatus(NO_INIT), mControl(0)
-{
- init(conn);
-}
-void SurfaceClient::init(const sp<IBinder>& conn)
-{
- mComposerService = getComposerService();
- sp<ISurfaceComposerClient> sf(interface_cast<ISurfaceComposerClient>(conn));
- if (sf != 0) {
- mConnection = conn;
- mControlMemory = sf->getControlBlock();
- mControl = static_cast<SharedClient *>(mControlMemory->getBase());
- mStatus = NO_ERROR;
- }
-}
-status_t SurfaceClient::initCheck() const {
- return mStatus;
-}
-SharedClient* SurfaceClient::getSharedClient() const {
- return mControl;
-}
-void SurfaceClient::signalServer() const {
- mComposerService->signal();
-}
-
-// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 35e4af3..3ddde38 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -38,7 +38,7 @@
GraphicBuffer::GraphicBuffer()
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mVStride(0), mIndex(-1)
+ mInitCheck(NO_ERROR), mIndex(-1)
{
width =
height =
@@ -51,7 +51,7 @@
GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h,
PixelFormat reqFormat, uint32_t reqUsage)
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mVStride(0), mIndex(-1)
+ mInitCheck(NO_ERROR), mIndex(-1)
{
width =
height =
@@ -67,7 +67,7 @@
uint32_t inStride, native_handle_t* inHandle, bool keepOwnership)
: BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mVStride(0), mIndex(-1)
+ mInitCheck(NO_ERROR), mIndex(-1)
{
width = w;
height = h;
@@ -141,7 +141,6 @@
this->height = h;
this->format = format;
this->usage = reqUsage;
- mVStride = 0;
}
return err;
}
@@ -182,7 +181,6 @@
sur->height = height;
sur->stride = stride;
sur->format = format;
- sur->vstride = mVStride;
sur->data = static_cast<GGLubyte*>(vaddr);
}
return res;
@@ -276,14 +274,6 @@
return mIndex;
}
-void GraphicBuffer::setVerticalStride(uint32_t vstride) {
- mVStride = vstride;
-}
-
-uint32_t GraphicBuffer::getVerticalStride() const {
- return mVStride;
-}
-
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index f1da72f..6e527d9 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -65,8 +65,9 @@
public static final int FILE_TYPE_WMV = 25;
public static final int FILE_TYPE_ASF = 26;
public static final int FILE_TYPE_MKV = 27;
+ public static final int FILE_TYPE_MP2TS = 28;
private static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4;
- private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_MKV;
+ private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_MP2TS;
// Image file types
public static final int FILE_TYPE_JPEG = 31;
@@ -156,6 +157,8 @@
addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2");
addFileType("MKV", FILE_TYPE_MKV, "video/x-matroska");
addFileType("WEBM", FILE_TYPE_MKV, "video/x-matroska");
+ addFileType("TS", FILE_TYPE_MP2TS, "video/mp2ts");
+
if (isWMVEnabled()) {
addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv");
addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");
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 35a10010..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/Android.mk b/media/libstagefright/Android.mk
index f00fad8..6286046 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -80,11 +80,13 @@
libstagefright_matroska \
libstagefright_vpxdec \
libvpx \
+ libstagefright_mpeg2ts \
LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \
libstagefright_enc_common \
- libstagefright_avc_common
+ libstagefright_avc_common \
+ libstagefright_foundation \
ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index c8834f8..264ac5f 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -75,6 +75,26 @@
}
}
+static int32_t getColorFormat(const char* colorFormat) {
+ if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422SP)) {
+ return OMX_COLOR_FormatYUV422SemiPlanar;
+ }
+
+ if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420SP)) {
+ return OMX_COLOR_FormatYUV420SemiPlanar;
+ }
+
+ if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422I)) {
+ return OMX_COLOR_FormatYCbYCr;
+ }
+
+ if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_RGB565)) {
+ return OMX_COLOR_Format16bitRGB565;
+ }
+
+ CHECK_EQ(0, "Unknown color format");
+}
+
// static
CameraSource *CameraSource::Create() {
sp<Camera> camera = Camera::connect(0);
@@ -97,8 +117,6 @@
CameraSource::CameraSource(const sp<Camera> &camera)
: mCamera(camera),
- mWidth(0),
- mHeight(0),
mFirstFrameTimeUs(0),
mLastFrameTimestampUs(0),
mNumFramesReceived(0),
@@ -108,8 +126,19 @@
String8 s = mCamera->getParameters();
printf("params: \"%s\"\n", s.string());
+ int32_t width, height;
CameraParameters params(s);
- params.getPreviewSize(&mWidth, &mHeight);
+ params.getPreviewSize(&width, &height);
+
+ const char *colorFormatStr = params.get(CameraParameters::KEY_VIDEO_FRAME_FORMAT);
+ CHECK(colorFormatStr != NULL);
+ int32_t colorFormat = getColorFormat(colorFormatStr);
+
+ mMeta = new MetaData;
+ mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+ mMeta->setInt32(kKeyColorFormat, colorFormat);
+ mMeta->setInt32(kKeyWidth, width);
+ mMeta->setInt32(kKeyHeight, height);
}
CameraSource::~CameraSource() {
@@ -163,13 +192,7 @@
}
sp<MetaData> CameraSource::getFormat() {
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
- meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420SemiPlanar);
- meta->setInt32(kKeyWidth, mWidth);
- meta->setInt32(kKeyHeight, mHeight);
-
- return meta;
+ return mMeta;
}
void CameraSource::signalBufferReturned(MediaBuffer *buffer) {
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 475422d..b569a6b 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -19,6 +19,7 @@
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
+#include "include/MPEG2TSExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -97,6 +98,7 @@
RegisterSniffer(SniffWAV);
RegisterSniffer(SniffOgg);
RegisterSniffer(SniffMatroska);
+ RegisterSniffer(SniffMPEG2TS);
}
// static
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 327a0ce..39d264c 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -38,5 +38,6 @@
const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav";
const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg";
const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska";
+const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts";
} // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 376d715..56e6136 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -23,6 +23,7 @@
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
+#include "include/MPEG2TSExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -73,6 +74,8 @@
return new OggExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {
return new MatroskaExtractor(source);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
+ return new MPEG2TSExtractor(source);
}
return NULL;
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/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 2829638..1629e9f 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -39,7 +39,7 @@
".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
- ".mkv", ".mka", ".webm"
+ ".mkv", ".mka", ".webm", ".ts"
};
static const size_t kNumValidExtensions =
sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
diff --git a/media/libstagefright/foundation/AAtomizer.cpp b/media/libstagefright/foundation/AAtomizer.cpp
new file mode 100644
index 0000000..b7b9e9f9
--- /dev/null
+++ b/media/libstagefright/foundation/AAtomizer.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#include <sys/types.h>
+
+#include "AAtomizer.h"
+
+namespace android {
+
+// static
+AAtomizer AAtomizer::gAtomizer;
+
+// static
+const char *AAtomizer::Atomize(const char *name) {
+ return gAtomizer.atomize(name);
+}
+
+AAtomizer::AAtomizer() {
+ for (size_t i = 0; i < 128; ++i) {
+ mAtoms.push(List<AString>());
+ }
+}
+
+const char *AAtomizer::atomize(const char *name) {
+ Mutex::Autolock autoLock(mLock);
+
+ const size_t n = mAtoms.size();
+ size_t index = AAtomizer::Hash(name) % n;
+ List<AString> &entry = mAtoms.editItemAt(index);
+ List<AString>::iterator it = entry.begin();
+ while (it != entry.end()) {
+ if ((*it) == name) {
+ return (*it).c_str();
+ }
+ ++it;
+ }
+
+ entry.push_back(AString(name));
+
+ return (*--entry.end()).c_str();
+}
+
+// static
+uint32_t AAtomizer::Hash(const char *s) {
+ uint32_t sum = 0;
+ while (*s != '\0') {
+ sum = (sum * 31) + *s;
+ ++s;
+ }
+
+ return sum;
+}
+
+} // namespace android
diff --git a/media/libstagefright/foundation/ABuffer.cpp b/media/libstagefright/foundation/ABuffer.cpp
new file mode 100644
index 0000000..6173db4
--- /dev/null
+++ b/media/libstagefright/foundation/ABuffer.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#include "ABuffer.h"
+
+#include "ADebug.h"
+#include "ALooper.h"
+#include "AMessage.h"
+
+namespace android {
+
+ABuffer::ABuffer(size_t capacity)
+ : mData(malloc(capacity)),
+ mCapacity(capacity),
+ mRangeOffset(0),
+ mRangeLength(capacity),
+ mInt32Data(0),
+ mOwnsData(true) {
+}
+
+ABuffer::ABuffer(void *data, size_t capacity)
+ : mData(data),
+ mCapacity(capacity),
+ mRangeOffset(0),
+ mRangeLength(capacity),
+ mInt32Data(0),
+ mOwnsData(false) {
+}
+
+ABuffer::~ABuffer() {
+ if (mOwnsData) {
+ if (mData != NULL) {
+ free(mData);
+ mData = NULL;
+ }
+ }
+
+ if (mFarewell != NULL) {
+ mFarewell->post();
+ }
+}
+
+void ABuffer::setRange(size_t offset, size_t size) {
+ CHECK_LE(offset, mCapacity);
+ CHECK_LE(offset + size, mCapacity);
+
+ mRangeOffset = offset;
+ mRangeLength = size;
+}
+
+void ABuffer::setFarewellMessage(const sp<AMessage> msg) {
+ mFarewell = msg;
+}
+
+sp<AMessage> ABuffer::meta() {
+ if (mMeta == NULL) {
+ mMeta = new AMessage;
+ }
+ return mMeta;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/foundation/ADebug.cpp b/media/libstagefright/foundation/ADebug.cpp
new file mode 100644
index 0000000..16f8b22
--- /dev/null
+++ b/media/libstagefright/foundation/ADebug.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#include "ADebug.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef ANDROID
+#include <cutils/log.h>
+#endif
+
+namespace android {
+
+Logger::Logger(LogType type)
+ : mLogType(type) {
+ switch (mLogType) {
+ case VERBOSE:
+ mMessage = "V ";
+ break;
+ case INFO:
+ mMessage = "I ";
+ break;
+ case WARNING:
+ mMessage = "W ";
+ break;
+ case ERROR:
+ mMessage = "E ";
+ break;
+ case FATAL:
+ mMessage = "F ";
+ break;
+
+ default:
+ break;
+ }
+}
+
+Logger::~Logger() {
+ if (mLogType == VERBOSE) {
+ return;
+ }
+
+ mMessage.append("\n");
+
+#if defined(ANDROID) && 1
+ LOG_PRI(ANDROID_LOG_INFO, "ADebug", "%s", mMessage.c_str());
+#else
+ fprintf(stderr, mMessage.c_str());
+ fflush(stderr);
+#endif
+
+ if (mLogType == FATAL) {
+ abort();
+ }
+}
+
+const char *LeafName(const char *s) {
+ const char *lastSlash = strrchr(s, '/');
+ return lastSlash != NULL ? lastSlash + 1 : s;
+}
+
+} // namespace android
diff --git a/media/libstagefright/foundation/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp
new file mode 100644
index 0000000..831fa2a
--- /dev/null
+++ b/media/libstagefright/foundation/ALooper.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ALooper"
+#include <utils/Log.h>
+
+#include <sys/time.h>
+
+#include "ALooper.h"
+
+#include "AHandler.h"
+#include "ALooperRoster.h"
+#include "AMessage.h"
+
+namespace android {
+
+ALooperRoster gLooperRoster;
+
+struct ALooper::LooperThread : public Thread {
+ LooperThread(ALooper *looper)
+ : mLooper(looper) {
+ }
+
+ virtual bool threadLoop() {
+ return mLooper->loop();
+ }
+
+protected:
+ virtual ~LooperThread() {}
+
+private:
+ ALooper *mLooper;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LooperThread);
+};
+
+// static
+int64_t ALooper::GetNowUs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ return (int64_t)tv.tv_sec * 1000000ll + tv.tv_usec;
+}
+
+ALooper::ALooper()
+ : mRunningLocally(false) {
+}
+
+ALooper::~ALooper() {
+ stop();
+}
+
+ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
+ return gLooperRoster.registerHandler(this, handler);
+}
+
+void ALooper::unregisterHandler(handler_id handlerID) {
+ gLooperRoster.unregisterHandler(handlerID);
+}
+
+status_t ALooper::start(bool runOnCallingThread) {
+ if (runOnCallingThread) {
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mThread != NULL || mRunningLocally) {
+ return INVALID_OPERATION;
+ }
+
+ mRunningLocally = true;
+ }
+
+ do {
+ } while (loop());
+
+ return OK;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (mThread != NULL || mRunningLocally) {
+ return INVALID_OPERATION;
+ }
+
+ mThread = new LooperThread(this);
+
+ status_t err = mThread->run("ALooper");
+ if (err != OK) {
+ mThread.clear();
+ }
+
+ return err;
+}
+
+status_t ALooper::stop() {
+ sp<LooperThread> thread;
+ bool runningLocally;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ thread = mThread;
+ runningLocally = mRunningLocally;
+ mThread.clear();
+ mRunningLocally = false;
+ }
+
+ if (thread == NULL && !runningLocally) {
+ return INVALID_OPERATION;
+ }
+
+ if (thread != NULL) {
+ thread->requestExit();
+ }
+
+ mQueueChangedCondition.signal();
+
+ if (!runningLocally) {
+ thread->requestExitAndWait();
+ }
+
+ return OK;
+}
+
+void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
+ Mutex::Autolock autoLock(mLock);
+
+ int64_t whenUs;
+ if (delayUs > 0) {
+ whenUs = GetNowUs() + delayUs;
+ } else {
+ whenUs = GetNowUs();
+ }
+
+ List<Event>::iterator it = mEventQueue.begin();
+ while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
+ ++it;
+ }
+
+ Event event;
+ event.mWhenUs = whenUs;
+ event.mMessage = msg;
+
+ if (it == mEventQueue.begin()) {
+ mQueueChangedCondition.signal();
+ }
+
+ mEventQueue.insert(it, event);
+}
+
+bool ALooper::loop() {
+ Event event;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (mThread == NULL && !mRunningLocally) {
+ return false;
+ }
+ if (mEventQueue.empty()) {
+ mQueueChangedCondition.wait(mLock);
+ return true;
+ }
+ int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
+ int64_t nowUs = GetNowUs();
+
+ if (whenUs > nowUs) {
+ int64_t delayUs = whenUs - nowUs;
+ mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
+
+ return true;
+ }
+
+ event = *mEventQueue.begin();
+ mEventQueue.erase(mEventQueue.begin());
+ }
+
+ gLooperRoster.deliverMessage(event.mMessage);
+
+ return true;
+}
+
+} // namespace android
diff --git a/media/libstagefright/foundation/ALooperRoster.cpp b/media/libstagefright/foundation/ALooperRoster.cpp
new file mode 100644
index 0000000..5bb1cf9
--- /dev/null
+++ b/media/libstagefright/foundation/ALooperRoster.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ALooperRoster"
+#include <utils/Log.h>
+
+#include "ALooperRoster.h"
+
+#include "ADebug.h"
+#include "AHandler.h"
+#include "AMessage.h"
+
+namespace android {
+
+ALooperRoster::ALooperRoster()
+ : mNextHandlerID(1) {
+}
+
+ALooper::handler_id ALooperRoster::registerHandler(
+ const sp<ALooper> looper, const sp<AHandler> &handler) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (handler->id() != 0) {
+ CHECK(!"A handler must only be registered once.");
+ return INVALID_OPERATION;
+ }
+
+ HandlerInfo info;
+ info.mLooper = looper;
+ info.mHandler = handler;
+ ALooper::handler_id handlerID = mNextHandlerID++;
+ mHandlers.add(handlerID, info);
+
+ handler->setID(handlerID);
+
+ return handlerID;
+}
+
+void ALooperRoster::unregisterHandler(ALooper::handler_id handlerID) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mHandlers.indexOfKey(handlerID);
+ CHECK(index >= 0);
+
+ const HandlerInfo &info = mHandlers.valueAt(index);
+ info.mHandler->setID(0);
+
+ mHandlers.removeItemsAt(index);
+}
+
+void ALooperRoster::postMessage(
+ const sp<AMessage> &msg, int64_t delayUs) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mHandlers.indexOfKey(msg->target());
+
+ if (index < 0) {
+ LOG(WARNING) << "failed to post message. Target handler not registered.";
+ return;
+ }
+
+ const HandlerInfo &info = mHandlers.valueAt(index);
+ info.mLooper->post(msg, delayUs);
+}
+
+void ALooperRoster::deliverMessage(const sp<AMessage> &msg) {
+ sp<AHandler> handler;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mHandlers.indexOfKey(msg->target());
+
+ if (index < 0) {
+ LOG(WARNING) << "failed to deliver message. Target handler not registered.";
+ return;
+ }
+
+ const HandlerInfo &info = mHandlers.valueAt(index);
+ handler = info.mHandler;
+ }
+
+ handler->onMessageReceived(msg);
+}
+
+} // namespace android
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
new file mode 100644
index 0000000..dfd1ae3
--- /dev/null
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -0,0 +1,241 @@
+/*
+ * 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.
+ */
+
+#include "AMessage.h"
+
+#include "AAtomizer.h"
+#include "ADebug.h"
+#include "ALooperRoster.h"
+#include "AString.h"
+
+namespace android {
+
+AMessage::AMessage(uint32_t what, ALooper::handler_id target)
+ : mWhat(what),
+ mTarget(target),
+ mNumItems(0) {
+}
+
+AMessage::~AMessage() {
+ clear();
+}
+
+void AMessage::setWhat(uint32_t what) {
+ mWhat = what;
+}
+
+uint32_t AMessage::what() const {
+ return mWhat;
+}
+
+void AMessage::setTarget(ALooper::handler_id handlerID) {
+ mTarget = handlerID;
+}
+
+ALooper::handler_id AMessage::target() const {
+ return mTarget;
+}
+
+void AMessage::clear() {
+ for (size_t i = 0; i < mNumItems; ++i) {
+ Item *item = &mItems[i];
+ freeItem(item);
+ }
+ mNumItems = 0;
+}
+
+void AMessage::freeItem(Item *item) {
+ switch (item->mType) {
+ case kTypeString:
+ {
+ delete item->u.stringValue;
+ break;
+ }
+
+ case kTypeObject:
+ case kTypeMessage:
+ {
+ if (item->u.refValue != NULL) {
+ item->u.refValue->decStrong(this);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+AMessage::Item *AMessage::allocateItem(const char *name) {
+ name = AAtomizer::Atomize(name);
+
+ size_t i = 0;
+ while (i < mNumItems && mItems[i].mName != name) {
+ ++i;
+ }
+
+ Item *item;
+
+ if (i < mNumItems) {
+ item = &mItems[i];
+ freeItem(item);
+ } else {
+ CHECK(mNumItems < kMaxNumItems);
+ i = mNumItems++;
+ item = &mItems[i];
+
+ item->mName = name;
+ }
+
+ return item;
+}
+
+const AMessage::Item *AMessage::findItem(
+ const char *name, Type type) const {
+ name = AAtomizer::Atomize(name);
+
+ for (size_t i = 0; i < mNumItems; ++i) {
+ const Item *item = &mItems[i];
+
+ if (item->mName == name) {
+ return item->mType == type ? item : NULL;
+ }
+ }
+
+ return NULL;
+}
+
+#define BASIC_TYPE(NAME,FIELDNAME,TYPENAME) \
+void AMessage::set##NAME(const char *name, TYPENAME value) { \
+ Item *item = allocateItem(name); \
+ \
+ item->mType = kType##NAME; \
+ item->u.FIELDNAME = value; \
+} \
+ \
+bool AMessage::find##NAME(const char *name, TYPENAME *value) const { \
+ const Item *item = findItem(name, kType##NAME); \
+ if (item) { \
+ *value = item->u.FIELDNAME; \
+ return true; \
+ } \
+ return false; \
+}
+
+BASIC_TYPE(Int32,int32Value,int32_t)
+BASIC_TYPE(Int64,int64Value,int64_t)
+BASIC_TYPE(Size,sizeValue,size_t)
+BASIC_TYPE(Float,floatValue,float)
+BASIC_TYPE(Double,doubleValue,double)
+BASIC_TYPE(Pointer,ptrValue,void *)
+
+#undef BASIC_TYPE
+
+void AMessage::setString(
+ const char *name, const char *s, ssize_t len) {
+ Item *item = allocateItem(name);
+ item->mType = kTypeString;
+ item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len);
+}
+
+void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
+ Item *item = allocateItem(name);
+ item->mType = kTypeObject;
+
+ if (obj != NULL) { obj->incStrong(this); }
+ item->u.refValue = obj.get();
+}
+
+void AMessage::setMessage(const char *name, const sp<AMessage> &obj) {
+ Item *item = allocateItem(name);
+ item->mType = kTypeMessage;
+
+ if (obj != NULL) { obj->incStrong(this); }
+ item->u.refValue = obj.get();
+}
+
+bool AMessage::findString(const char *name, AString *value) const {
+ const Item *item = findItem(name, kTypeString);
+ if (item) {
+ *value = *item->u.stringValue;
+ return true;
+ }
+ return false;
+}
+
+bool AMessage::findObject(const char *name, sp<RefBase> *obj) const {
+ const Item *item = findItem(name, kTypeObject);
+ if (item) {
+ *obj = item->u.refValue;
+ return true;
+ }
+ return false;
+}
+
+bool AMessage::findMessage(const char *name, sp<AMessage> *obj) const {
+ const Item *item = findItem(name, kTypeMessage);
+ if (item) {
+ *obj = static_cast<AMessage *>(item->u.refValue);
+ return true;
+ }
+ return false;
+}
+
+void AMessage::post(int64_t delayUs) {
+ extern ALooperRoster gLooperRoster;
+
+ gLooperRoster.postMessage(this, delayUs);
+}
+
+sp<AMessage> AMessage::dup() const {
+ sp<AMessage> msg = new AMessage(mWhat, mTarget);
+ msg->mNumItems = mNumItems;
+
+ for (size_t i = 0; i < mNumItems; ++i) {
+ const Item *from = &mItems[i];
+ Item *to = &msg->mItems[i];
+
+ to->mName = from->mName;
+ to->mType = from->mType;
+
+ switch (from->mType) {
+ case kTypeString:
+ {
+ to->u.stringValue =
+ new AString(*from->u.stringValue);
+ break;
+ }
+
+ case kTypeObject:
+ case kTypeMessage:
+ {
+ to->u.refValue = from->u.refValue;
+ to->u.refValue->incStrong(msg.get());
+ break;
+ }
+
+ default:
+ {
+ to->u = from->u;
+ break;
+ }
+ }
+ }
+
+ return msg;
+}
+
+} // namespace android
diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp
new file mode 100644
index 0000000..61b76cf
--- /dev/null
+++ b/media/libstagefright/foundation/AString.cpp
@@ -0,0 +1,331 @@
+/*
+ * 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.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ADebug.h"
+#include "AString.h"
+
+namespace android {
+
+// static
+const char *AString::kEmptyString = "";
+
+AString::AString()
+ : mData((char *)kEmptyString),
+ mSize(0),
+ mAllocSize(1) {
+}
+
+AString::AString(const char *s)
+ : mData(NULL),
+ mSize(0),
+ mAllocSize(1) {
+ setTo(s);
+}
+
+AString::AString(const char *s, size_t size)
+ : mData(NULL),
+ mSize(0),
+ mAllocSize(1) {
+ setTo(s, size);
+}
+
+AString::AString(const AString &from)
+ : mData(NULL),
+ mSize(0),
+ mAllocSize(1) {
+ setTo(from, 0, from.size());
+}
+
+AString::AString(const AString &from, size_t offset, size_t n)
+ : mData(NULL),
+ mSize(0),
+ mAllocSize(1) {
+ setTo(from, offset, n);
+}
+
+AString::~AString() {
+ clear();
+}
+
+AString &AString::operator=(const AString &from) {
+ if (&from != this) {
+ setTo(from, 0, from.size());
+ }
+
+ return *this;
+}
+
+size_t AString::size() const {
+ return mSize;
+}
+
+const char *AString::c_str() const {
+ return mData;
+}
+
+bool AString::empty() const {
+ return mSize == 0;
+}
+
+void AString::setTo(const char *s) {
+ setTo(s, strlen(s));
+}
+
+void AString::setTo(const char *s, size_t size) {
+ clear();
+ append(s, size);
+}
+
+void AString::setTo(const AString &from, size_t offset, size_t n) {
+ CHECK(&from != this);
+
+ clear();
+ setTo(from.mData + offset, n);
+}
+
+void AString::clear() {
+ if (mData && mData != kEmptyString) {
+ free(mData);
+ mData = NULL;
+ }
+
+ mData = (char *)kEmptyString;
+ mSize = 0;
+ mAllocSize = 1;
+}
+
+size_t AString::hash() const {
+ size_t x = 0;
+ for (size_t i = 0; i < mSize; ++i) {
+ x = (x * 31) + mData[i];
+ }
+
+ return x;
+}
+
+bool AString::operator==(const AString &other) const {
+ return mSize == other.mSize && !memcmp(mData, other.mData, mSize);
+}
+
+void AString::trim() {
+ makeMutable();
+
+ size_t i = 0;
+ while (i < mSize && isspace(mData[i])) {
+ ++i;
+ }
+
+ size_t j = mSize;
+ while (j > i && isspace(mData[j - 1])) {
+ --j;
+ }
+
+ memmove(mData, &mData[i], j - i);
+ mSize = j - i;
+ mData[mSize] = '\0';
+}
+
+void AString::erase(size_t start, size_t n) {
+ CHECK_LT(start, mSize);
+ CHECK_LE(start + n, mSize);
+
+ makeMutable();
+
+ memmove(&mData[start], &mData[start + n], mSize - start - n);
+ mSize -= n;
+ mData[mSize] = '\0';
+}
+
+void AString::makeMutable() {
+ if (mData == kEmptyString) {
+ mData = strdup(kEmptyString);
+ }
+}
+
+void AString::append(const char *s) {
+ append(s, strlen(s));
+}
+
+void AString::append(const char *s, size_t size) {
+ makeMutable();
+
+ if (mSize + size + 1 > mAllocSize) {
+ mAllocSize = (mAllocSize + size + 31) & -32;
+ mData = (char *)realloc(mData, mAllocSize);
+ CHECK(mData != NULL);
+ }
+
+ memcpy(&mData[mSize], s, size);
+ mSize += size;
+ mData[mSize] = '\0';
+}
+
+void AString::append(const AString &from) {
+ append(from.c_str(), from.size());
+}
+
+void AString::append(const AString &from, size_t offset, size_t n) {
+ append(from.c_str() + offset, n);
+}
+
+void AString::append(int x) {
+ char s[16];
+ sprintf(s, "%d", x);
+
+ append(s);
+}
+
+void AString::append(unsigned x) {
+ char s[16];
+ sprintf(s, "%u", x);
+
+ append(s);
+}
+
+void AString::append(long x) {
+ char s[16];
+ sprintf(s, "%ld", x);
+
+ append(s);
+}
+
+void AString::append(unsigned long x) {
+ char s[16];
+ sprintf(s, "%lu", x);
+
+ append(s);
+}
+
+void AString::append(long long x) {
+ char s[32];
+ sprintf(s, "%lld", x);
+
+ append(s);
+}
+
+void AString::append(unsigned long long x) {
+ char s[32];
+ sprintf(s, "%llu", x);
+
+ append(s);
+}
+
+void AString::append(float x) {
+ char s[16];
+ sprintf(s, "%f", x);
+
+ append(s);
+}
+
+void AString::append(double x) {
+ char s[16];
+ sprintf(s, "%f", x);
+
+ append(s);
+}
+
+void AString::append(void *x) {
+ char s[16];
+ sprintf(s, "%p", x);
+
+ append(s);
+}
+
+ssize_t AString::find(const char *substring, size_t start) const {
+ CHECK_LE(start, size());
+
+ const char *match = strstr(mData + start, substring);
+
+ if (match == NULL) {
+ return -1;
+ }
+
+ return match - mData;
+}
+
+void AString::insert(const AString &from, size_t insertionPos) {
+ insert(from.c_str(), from.size(), insertionPos);
+}
+
+void AString::insert(const char *from, size_t size, size_t insertionPos) {
+ CHECK_GE(insertionPos, 0u);
+ CHECK_LE(insertionPos, mSize);
+
+ makeMutable();
+
+ if (mSize + size + 1 > mAllocSize) {
+ mAllocSize = (mAllocSize + size + 31) & -32;
+ mData = (char *)realloc(mData, mAllocSize);
+ CHECK(mData != NULL);
+ }
+
+ memmove(&mData[insertionPos + size],
+ &mData[insertionPos], mSize - insertionPos + 1);
+
+ memcpy(&mData[insertionPos], from, size);
+
+ mSize += size;
+}
+
+bool AString::operator<(const AString &other) const {
+ return compare(other) < 0;
+}
+
+bool AString::operator>(const AString &other) const {
+ return compare(other) > 0;
+}
+
+int AString::compare(const AString &other) const {
+ return strcmp(mData, other.mData);
+}
+
+void AString::tolower() {
+ makeMutable();
+
+ for (size_t i = 0; i < mSize; ++i) {
+ mData[i] = ::tolower(mData[i]);
+ }
+}
+
+bool AString::startsWith(const char *prefix) const {
+ return !strncmp(mData, prefix, strlen(prefix));
+}
+
+AString StringPrintf(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+
+ char *buffer;
+ vasprintf(&buffer, format, ap);
+
+ va_end(ap);
+
+ AString result(buffer);
+
+ free(buffer);
+ buffer = NULL;
+
+ return result;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
new file mode 100644
index 0000000..73047e7
--- /dev/null
+++ b/media/libstagefright/foundation/Android.mk
@@ -0,0 +1,35 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ AAtomizer.cpp \
+ ABuffer.cpp \
+ ADebug.cpp \
+ ALooper.cpp \
+ ALooperRoster.cpp \
+ AMessage.cpp \
+ AString.cpp \
+ base64.cpp \
+ hexdump.cpp
+
+LOCAL_C_INCLUDES:= \
+ frameworks/base/include/media/stagefright/foundation
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libmedia \
+ libutils \
+ libcutils \
+ libui \
+ libsonivox \
+ libvorbisidec \
+ libsurfaceflinger_client \
+ libcamera_client
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE:= libstagefright_foundation
+
+LOCAL_PRELINK_MODULE:= false
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/foundation/base64.cpp b/media/libstagefright/foundation/base64.cpp
new file mode 100644
index 0000000..d5fb4e01
--- /dev/null
+++ b/media/libstagefright/foundation/base64.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+#include "base64.h"
+
+#include "ABuffer.h"
+#include "ADebug.h"
+
+namespace android {
+
+sp<ABuffer> decodeBase64(const AString &s) {
+ if ((s.size() % 4) != 0) {
+ return NULL;
+ }
+
+ size_t n = s.size();
+ size_t padding = 0;
+ if (n >= 1 && s.c_str()[n - 1] == '=') {
+ padding = 1;
+
+ if (n >= 2 && s.c_str()[n - 2] == '=') {
+ padding = 2;
+ }
+ }
+
+ size_t outLen = 3 * s.size() / 4 - padding;
+
+ sp<ABuffer> buffer = new ABuffer(outLen);
+
+ uint8_t *out = buffer->data();
+ size_t j = 0;
+ uint32_t accum = 0;
+ for (size_t i = 0; i < n; ++i) {
+ char c = s.c_str()[i];
+ unsigned value;
+ if (c >= 'A' && c <= 'Z') {
+ value = c - 'A';
+ } else if (c >= 'a' && c <= 'z') {
+ value = 26 + c - 'a';
+ } else if (c >= '0' && c <= '9') {
+ value = 52 + c - '0';
+ } else if (c == '+') {
+ value = 62;
+ } else if (c == '/') {
+ value = 63;
+ } else if (c != '=') {
+ return NULL;
+ } else {
+ if (i < n - padding) {
+ return NULL;
+ }
+
+ value = 0;
+ }
+
+ accum = (accum << 6) | value;
+
+ if (((i + 1) % 4) == 0) {
+ out[j++] = (accum >> 16);
+
+ if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
+ if (j < outLen) { out[j++] = accum & 0xff; }
+
+ accum = 0;
+ }
+ }
+
+ return buffer;
+}
+
+static char encode6Bit(unsigned x) {
+ if (x <= 25) {
+ return 'A' + x;
+ } else if (x <= 51) {
+ return 'a' + x - 26;
+ } else if (x <= 61) {
+ return '0' + x - 52;
+ } else if (x == 62) {
+ return '+';
+ } else {
+ return '/';
+ }
+}
+
+void encodeBase64(
+ const void *_data, size_t size, AString *out) {
+ out->clear();
+
+ const uint8_t *data = (const uint8_t *)_data;
+
+ size_t i;
+ for (i = 0; i < (size / 3) * 3; i += 3) {
+ uint8_t x1 = data[i];
+ uint8_t x2 = data[i + 1];
+ uint8_t x3 = data[i + 2];
+
+ out->append(encode6Bit(x1 >> 2));
+ out->append(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f));
+ out->append(encode6Bit((x2 << 2 | x3 >> 6) & 0x3f));
+ out->append(encode6Bit(x3 & 0x3f));
+ }
+ switch (size % 3) {
+ case 0:
+ break;
+ case 2:
+ {
+ uint8_t x1 = data[i];
+ uint8_t x2 = data[i + 1];
+ out->append(encode6Bit(x1 >> 2));
+ out->append(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f));
+ out->append(encode6Bit((x2 << 2) & 0x3f));
+ out->append('=');
+ break;
+ }
+ default:
+ {
+ uint8_t x1 = data[i];
+ out->append(encode6Bit(x1 >> 2));
+ out->append(encode6Bit((x1 << 4) & 0x3f));
+ out->append("==");
+ break;
+ }
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/foundation/hexdump.cpp b/media/libstagefright/foundation/hexdump.cpp
new file mode 100644
index 0000000..093b587
--- /dev/null
+++ b/media/libstagefright/foundation/hexdump.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#include "hexdump.h"
+
+#include "ADebug.h"
+#include "AString.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+
+namespace android {
+
+void hexdump(const void *_data, size_t size) {
+ const uint8_t *data = (const uint8_t *)_data;
+
+ size_t offset = 0;
+ while (offset < size) {
+ AString line;
+
+ char tmp[32];
+ sprintf(tmp, "%08lx: ", (unsigned long)offset);
+
+ line.append(tmp);
+
+ for (size_t i = 0; i < 16; ++i) {
+ if (i == 8) {
+ line.append(' ');
+ }
+ if (offset + i >= size) {
+ line.append(" ");
+ } else {
+ sprintf(tmp, "%02x ", data[offset + i]);
+ line.append(tmp);
+ }
+ }
+
+ line.append(' ');
+
+ for (size_t i = 0; i < 16; ++i) {
+ if (offset + i >= size) {
+ break;
+ }
+
+ if (isprint(data[offset + i])) {
+ line.append((char)data[offset + i]);
+ } else {
+ line.append('.');
+ }
+ }
+
+ LOG(INFO) << line;
+
+ offset += 16;
+ }
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
new file mode 100644
index 0000000..c96973b
--- /dev/null
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -0,0 +1,54 @@
+#ifndef MPEG2_TS_EXTRACTOR_H_
+
+#define MPEG2_TS_EXTRACTOR_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <utils/threads.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct AnotherPacketSource;
+struct ATSParser;
+struct DataSource;
+struct MPEG2TSSource;
+struct String8;
+
+struct MPEG2TSExtractor : public MediaExtractor {
+ MPEG2TSExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+ virtual sp<MetaData> getMetaData();
+
+ virtual uint32_t flags() const {
+ return CAN_PAUSE;
+ }
+
+private:
+ friend struct MPEG2TSSource;
+
+ Mutex mLock;
+
+ sp<DataSource> mDataSource;
+ sp<ATSParser> mParser;
+
+ Vector<sp<AnotherPacketSource> > mSourceImpls;
+
+ off_t mOffset;
+
+ void init();
+ status_t feedMore();
+
+ DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSExtractor);
+};
+
+bool SniffMPEG2TS(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
+
+} // namespace android
+
+#endif // MPEG2_TS_EXTRACTOR_H_
diff --git a/media/libstagefright/mpeg2ts/ABitReader.cpp b/media/libstagefright/mpeg2ts/ABitReader.cpp
new file mode 100644
index 0000000..24c8df8
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/ABitReader.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#include "ABitReader.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+ABitReader::ABitReader(const uint8_t *data, size_t size)
+ : mData(data),
+ mSize(size),
+ mReservoir(0),
+ mNumBitsLeft(0) {
+}
+
+void ABitReader::fillReservoir() {
+ CHECK_GT(mSize, 0u);
+
+ mReservoir = 0;
+ size_t i;
+ for (i = 0; mSize > 0 && i < 4; ++i) {
+ mReservoir = (mReservoir << 8) | *mData;
+
+ ++mData;
+ --mSize;
+ }
+
+ mNumBitsLeft = 8 * i;
+ mReservoir <<= 32 - mNumBitsLeft;
+}
+
+uint32_t ABitReader::getBits(size_t n) {
+ CHECK_LE(n, 32u);
+
+ uint32_t result = 0;
+ while (n > 0) {
+ if (mNumBitsLeft == 0) {
+ fillReservoir();
+ }
+
+ size_t m = n;
+ if (m > mNumBitsLeft) {
+ m = mNumBitsLeft;
+ }
+
+ result = (result << m) | (mReservoir >> (32 - m));
+ mReservoir <<= m;
+ mNumBitsLeft -= m;
+
+ n -= m;
+ }
+
+ return result;
+}
+
+void ABitReader::skipBits(size_t n) {
+ while (n > 32) {
+ getBits(32);
+ n -= 32;
+ }
+
+ if (n > 0) {
+ getBits(n);
+ }
+}
+
+void ABitReader::putBits(uint32_t x, size_t n) {
+ CHECK_LE(mNumBitsLeft + n, 32u);
+
+ mReservoir = (mReservoir >> n) | (x << (32 - n));
+ mNumBitsLeft += n;
+}
+
+size_t ABitReader::numBitsLeft() const {
+ return mSize * 8 + mNumBitsLeft;
+}
+
+const uint8_t *ABitReader::data() const {
+ CHECK_EQ(mNumBitsLeft % 8, 0u);
+
+ return mData - mNumBitsLeft / 8;
+}
+
+} // namespace android
diff --git a/media/libstagefright/mpeg2ts/ABitReader.h b/media/libstagefright/mpeg2ts/ABitReader.h
new file mode 100644
index 0000000..5135211
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/ABitReader.h
@@ -0,0 +1,53 @@
+/*
+ * 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 A_BIT_READER_H_
+
+#define A_BIT_READER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+
+namespace android {
+
+struct ABitReader {
+ ABitReader(const uint8_t *data, size_t size);
+
+ uint32_t getBits(size_t n);
+ void skipBits(size_t n);
+
+ size_t numBitsLeft() const;
+
+ const uint8_t *data() const;
+
+private:
+ const uint8_t *mData;
+ size_t mSize;
+
+ uint32_t mReservoir; // left-aligned bits
+ size_t mNumBitsLeft;
+
+ void fillReservoir();
+ void putBits(uint32_t x, size_t n);
+
+ DISALLOW_EVIL_CONSTRUCTORS(ABitReader);
+};
+
+} // namespace android
+
+#endif // A_BIT_READER_H_
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
new file mode 100644
index 0000000..d05975d
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -0,0 +1,941 @@
+/*
+ * 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.
+ */
+
+#include "ATSParser.h"
+
+#include "ABitReader.h"
+#include "AnotherPacketSource.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+static const size_t kTSPacketSize = 188;
+
+struct ATSParser::Program : public RefBase {
+ Program(unsigned programMapPID);
+
+ bool parsePID(
+ unsigned pid, unsigned payload_unit_start_indicator,
+ ABitReader *br);
+
+ sp<MediaSource> getSource(SourceType type);
+
+private:
+ unsigned mProgramMapPID;
+ KeyedVector<unsigned, sp<Stream> > mStreams;
+
+ void parseProgramMap(ABitReader *br);
+
+ DISALLOW_EVIL_CONSTRUCTORS(Program);
+};
+
+struct ATSParser::Stream : public RefBase {
+ Stream(unsigned elementaryPID, unsigned streamType);
+
+ void parse(
+ unsigned payload_unit_start_indicator,
+ ABitReader *br);
+
+ sp<MediaSource> getSource(SourceType type);
+
+protected:
+ virtual ~Stream();
+
+private:
+ unsigned mElementaryPID;
+ unsigned mStreamType;
+
+ sp<ABuffer> mBuffer;
+ sp<AnotherPacketSource> mSource;
+ bool mPayloadStarted;
+
+ void flush();
+ void parsePES(ABitReader *br);
+
+ void onPayloadData(
+ unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
+ const uint8_t *data, size_t size);
+
+ DISALLOW_EVIL_CONSTRUCTORS(Stream);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+ATSParser::Program::Program(unsigned programMapPID)
+ : mProgramMapPID(programMapPID) {
+}
+
+bool ATSParser::Program::parsePID(
+ unsigned pid, unsigned payload_unit_start_indicator,
+ ABitReader *br) {
+ if (pid == mProgramMapPID) {
+ if (payload_unit_start_indicator) {
+ unsigned skip = br->getBits(8);
+ br->skipBits(skip * 8);
+ }
+
+ parseProgramMap(br);
+ return true;
+ }
+
+ ssize_t index = mStreams.indexOfKey(pid);
+ if (index < 0) {
+ return false;
+ }
+
+ mStreams.editValueAt(index)->parse(
+ payload_unit_start_indicator, br);
+
+ return true;
+}
+
+void ATSParser::Program::parseProgramMap(ABitReader *br) {
+ unsigned table_id = br->getBits(8);
+ LOG(VERBOSE) << " table_id = " << table_id;
+ CHECK_EQ(table_id, 0x02u);
+
+ unsigned section_syntax_indictor = br->getBits(1);
+ LOG(VERBOSE) << " section_syntax_indictor = " << section_syntax_indictor;
+ CHECK_EQ(section_syntax_indictor, 1u);
+
+ CHECK_EQ(br->getBits(1), 0u);
+ LOG(VERBOSE) << " reserved = " << br->getBits(2);
+
+ unsigned section_length = br->getBits(12);
+ LOG(VERBOSE) << " section_length = " << section_length;
+ CHECK((section_length & 0xc00) == 0);
+ CHECK_LE(section_length, 1021u);
+
+ LOG(VERBOSE) << " program_number = " << br->getBits(16);
+ LOG(VERBOSE) << " reserved = " << br->getBits(2);
+ LOG(VERBOSE) << " version_number = " << br->getBits(5);
+ LOG(VERBOSE) << " current_next_indicator = " << br->getBits(1);
+ LOG(VERBOSE) << " section_number = " << br->getBits(8);
+ LOG(VERBOSE) << " last_section_number = " << br->getBits(8);
+ LOG(VERBOSE) << " reserved = " << br->getBits(3);
+
+ LOG(VERBOSE) << " PCR_PID = "
+ << StringPrintf("0x%04x", br->getBits(13));
+
+ LOG(VERBOSE) << " reserved = " << br->getBits(4);
+
+ unsigned program_info_length = br->getBits(12);
+ LOG(VERBOSE) << " program_info_length = " << program_info_length;
+ CHECK((program_info_length & 0xc00) == 0);
+
+ br->skipBits(program_info_length * 8); // skip descriptors
+
+ // infoBytesRemaining is the number of bytes that make up the
+ // variable length section of ES_infos. It does not include the
+ // final CRC.
+ size_t infoBytesRemaining = section_length - 9 - program_info_length - 4;
+
+ while (infoBytesRemaining > 0) {
+ CHECK_GE(infoBytesRemaining, 5u);
+
+ unsigned streamType = br->getBits(8);
+ LOG(VERBOSE) << " stream_type = "
+ << StringPrintf("0x%02x", streamType);
+
+ LOG(VERBOSE) << " reserved = " << br->getBits(3);
+
+ unsigned elementaryPID = br->getBits(13);
+ LOG(VERBOSE) << " elementary_PID = "
+ << StringPrintf("0x%04x", elementaryPID);
+
+ LOG(VERBOSE) << " reserved = " << br->getBits(4);
+
+ unsigned ES_info_length = br->getBits(12);
+ LOG(VERBOSE) << " ES_info_length = " << ES_info_length;
+ CHECK((ES_info_length & 0xc00) == 0);
+
+ CHECK_GE(infoBytesRemaining - 5, ES_info_length);
+
+#if 0
+ br->skipBits(ES_info_length * 8); // skip descriptors
+#else
+ unsigned info_bytes_remaining = ES_info_length;
+ while (info_bytes_remaining >= 2) {
+ LOG(VERBOSE) << " tag = " << StringPrintf("0x%02x", br->getBits(8));
+
+ unsigned descLength = br->getBits(8);
+ LOG(VERBOSE) << " len = " << descLength;
+
+ CHECK_GE(info_bytes_remaining, 2 + descLength);
+
+ br->skipBits(descLength * 8);
+
+ info_bytes_remaining -= descLength + 2;
+ }
+ CHECK_EQ(info_bytes_remaining, 0u);
+#endif
+
+ ssize_t index = mStreams.indexOfKey(elementaryPID);
+#if 0 // XXX revisit
+ CHECK_LT(index, 0);
+ mStreams.add(elementaryPID, new Stream(elementaryPID, streamType));
+#else
+ if (index < 0) {
+ mStreams.add(elementaryPID, new Stream(elementaryPID, streamType));
+ }
+#endif
+
+ infoBytesRemaining -= 5 + ES_info_length;
+ }
+
+ CHECK_EQ(infoBytesRemaining, 0u);
+
+ LOG(VERBOSE) << " CRC = " << StringPrintf("0x%08x", br->getBits(32));
+}
+
+sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
+ for (size_t i = 0; i < mStreams.size(); ++i) {
+ sp<MediaSource> source = mStreams.editValueAt(i)->getSource(type);
+ if (source != NULL) {
+ return source;
+ }
+ }
+
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ATSParser::Stream::Stream(unsigned elementaryPID, unsigned streamType)
+ : mElementaryPID(elementaryPID),
+ mStreamType(streamType),
+ mBuffer(new ABuffer(65536)),
+ mPayloadStarted(false) {
+ mBuffer->setRange(0, 0);
+}
+
+ATSParser::Stream::~Stream() {
+}
+
+void ATSParser::Stream::parse(
+ unsigned payload_unit_start_indicator, ABitReader *br) {
+ if (payload_unit_start_indicator) {
+ if (mPayloadStarted) {
+ // Otherwise we run the danger of receiving the trailing bytes
+ // of a PES packet that we never saw the start of and assuming
+ // we have a a complete PES packet.
+
+ flush();
+ }
+
+ mPayloadStarted = true;
+ }
+
+ if (!mPayloadStarted) {
+ return;
+ }
+
+ size_t payloadSizeBits = br->numBitsLeft();
+ CHECK_EQ(payloadSizeBits % 8, 0u);
+
+ CHECK_LE(mBuffer->size() + payloadSizeBits / 8, mBuffer->capacity());
+
+ memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8);
+ mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
+}
+
+void ATSParser::Stream::parsePES(ABitReader *br) {
+ unsigned packet_startcode_prefix = br->getBits(24);
+
+ LOG(VERBOSE) << "packet_startcode_prefix = "
+ << StringPrintf("0x%08x", packet_startcode_prefix);
+
+ CHECK_EQ(packet_startcode_prefix, 0x000001u);
+
+ unsigned stream_id = br->getBits(8);
+ LOG(VERBOSE) << "stream_id = " << StringPrintf("0x%02x", stream_id);
+
+ unsigned PES_packet_length = br->getBits(16);
+ LOG(VERBOSE) << "PES_packet_length = " << PES_packet_length;
+
+ if (stream_id != 0xbc // program_stream_map
+ && stream_id != 0xbe // padding_stream
+ && stream_id != 0xbf // private_stream_2
+ && stream_id != 0xf0 // ECM
+ && stream_id != 0xf1 // EMM
+ && stream_id != 0xff // program_stream_directory
+ && stream_id != 0xf2 // DSMCC
+ && stream_id != 0xf8) { // H.222.1 type E
+ CHECK_EQ(br->getBits(2), 2u);
+
+ LOG(VERBOSE) << "PES_scrambling_control = " << br->getBits(2);
+ LOG(VERBOSE) << "PES_priority = " << br->getBits(1);
+ LOG(VERBOSE) << "data_alignment_indicator = " << br->getBits(1);
+ LOG(VERBOSE) << "copyright = " << br->getBits(1);
+ LOG(VERBOSE) << "original_or_copy = " << br->getBits(1);
+
+ unsigned PTS_DTS_flags = br->getBits(2);
+ LOG(VERBOSE) << "PTS_DTS_flags = " << PTS_DTS_flags;
+
+ unsigned ESCR_flag = br->getBits(1);
+ LOG(VERBOSE) << "ESCR_flag = " << ESCR_flag;
+
+ unsigned ES_rate_flag = br->getBits(1);
+ LOG(VERBOSE) << "ES_rate_flag = " << ES_rate_flag;
+
+ unsigned DSM_trick_mode_flag = br->getBits(1);
+ LOG(VERBOSE) << "DSM_trick_mode_flag = " << DSM_trick_mode_flag;
+
+ unsigned additional_copy_info_flag = br->getBits(1);
+ LOG(VERBOSE) << "additional_copy_info_flag = "
+ << additional_copy_info_flag;
+
+ LOG(VERBOSE) << "PES_CRC_flag = " << br->getBits(1);
+ LOG(VERBOSE) << "PES_extension_flag = " << br->getBits(1);
+
+ unsigned PES_header_data_length = br->getBits(8);
+ LOG(VERBOSE) << "PES_header_data_length = " << PES_header_data_length;
+
+ unsigned optional_bytes_remaining = PES_header_data_length;
+
+ uint64_t PTS = 0, DTS = 0;
+
+ if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
+ CHECK_GE(optional_bytes_remaining, 5u);
+
+ CHECK_EQ(br->getBits(4), PTS_DTS_flags);
+
+ PTS = ((uint64_t)br->getBits(3)) << 30;
+ CHECK_EQ(br->getBits(1), 1u);
+ PTS |= ((uint64_t)br->getBits(15)) << 15;
+ CHECK_EQ(br->getBits(1), 1u);
+ PTS |= br->getBits(15);
+ CHECK_EQ(br->getBits(1), 1u);
+
+ LOG(VERBOSE) << "PTS = " << PTS;
+ // LOG(INFO) << "PTS = " << PTS / 90000.0f << " secs";
+
+ optional_bytes_remaining -= 5;
+
+ if (PTS_DTS_flags == 3) {
+ CHECK_GE(optional_bytes_remaining, 5u);
+
+ CHECK_EQ(br->getBits(4), 1u);
+
+ DTS = ((uint64_t)br->getBits(3)) << 30;
+ CHECK_EQ(br->getBits(1), 1u);
+ DTS |= ((uint64_t)br->getBits(15)) << 15;
+ CHECK_EQ(br->getBits(1), 1u);
+ DTS |= br->getBits(15);
+ CHECK_EQ(br->getBits(1), 1u);
+
+ LOG(VERBOSE) << "DTS = " << DTS;
+
+ optional_bytes_remaining -= 5;
+ }
+ }
+
+ if (ESCR_flag) {
+ CHECK_GE(optional_bytes_remaining, 6u);
+
+ br->getBits(2);
+
+ uint64_t ESCR = ((uint64_t)br->getBits(3)) << 30;
+ CHECK_EQ(br->getBits(1), 1u);
+ ESCR |= ((uint64_t)br->getBits(15)) << 15;
+ CHECK_EQ(br->getBits(1), 1u);
+ ESCR |= br->getBits(15);
+ CHECK_EQ(br->getBits(1), 1u);
+
+ LOG(VERBOSE) << "ESCR = " << ESCR;
+ LOG(VERBOSE) << "ESCR_extension = " << br->getBits(9);
+
+ CHECK_EQ(br->getBits(1), 1u);
+
+ optional_bytes_remaining -= 6;
+ }
+
+ if (ES_rate_flag) {
+ CHECK_GE(optional_bytes_remaining, 3u);
+
+ CHECK_EQ(br->getBits(1), 1u);
+ LOG(VERBOSE) << "ES_rate = " << br->getBits(22);
+ CHECK_EQ(br->getBits(1), 1u);
+
+ optional_bytes_remaining -= 3;
+ }
+
+ br->skipBits(optional_bytes_remaining * 8);
+
+ // ES data follows.
+
+ onPayloadData(
+ PTS_DTS_flags, PTS, DTS,
+ br->data(), br->numBitsLeft() / 8);
+
+ if (PES_packet_length != 0) {
+ CHECK_GE(PES_packet_length, PES_header_data_length + 3);
+
+ unsigned dataLength =
+ PES_packet_length - 3 - PES_header_data_length;
+
+ CHECK_EQ(br->numBitsLeft(), dataLength * 8);
+
+ br->skipBits(dataLength * 8);
+ } else {
+ size_t payloadSizeBits = br->numBitsLeft();
+ CHECK((payloadSizeBits % 8) == 0);
+
+ LOG(VERBOSE) << "There's " << (payloadSizeBits / 8)
+ << " bytes of payload.";
+ }
+ } else if (stream_id == 0xbe) { // padding_stream
+ CHECK_NE(PES_packet_length, 0u);
+ br->skipBits(PES_packet_length * 8);
+ } else {
+ CHECK_NE(PES_packet_length, 0u);
+ br->skipBits(PES_packet_length * 8);
+ }
+}
+
+void ATSParser::Stream::flush() {
+ if (mBuffer->size() == 0) {
+ return;
+ }
+
+ LOG(VERBOSE) << "flushing stream "
+ << StringPrintf("0x%04x", mElementaryPID)
+ << " size = " << mBuffer->size();
+
+ ABitReader br(mBuffer->data(), mBuffer->size());
+ parsePES(&br);
+
+ mBuffer->setRange(0, 0);
+}
+
+static sp<ABuffer> FindNAL(
+ const uint8_t *data, size_t size, unsigned nalType,
+ size_t *stopOffset) {
+ bool foundStart = false;
+ size_t startOffset = 0;
+
+ size_t offset = 0;
+ for (;;) {
+ while (offset + 3 < size
+ && memcmp("\x00\x00\x00\x01", &data[offset], 4)) {
+ ++offset;
+ }
+
+ if (foundStart) {
+ size_t nalSize;
+ if (offset + 3 >= size) {
+ nalSize = size - startOffset;
+ } else {
+ nalSize = offset - startOffset;
+ }
+
+ sp<ABuffer> nal = new ABuffer(nalSize);
+ memcpy(nal->data(), &data[startOffset], nalSize);
+
+ if (stopOffset != NULL) {
+ *stopOffset = startOffset + nalSize;
+ }
+
+ return nal;
+ }
+
+ if (offset + 4 >= size) {
+ return NULL;
+ }
+
+ if ((data[offset + 4] & 0x1f) == nalType) {
+ foundStart = true;
+ startOffset = offset + 4;
+ }
+
+ offset += 4;
+ }
+}
+
+static unsigned parseUE(ABitReader *br) {
+ unsigned numZeroes = 0;
+ while (br->getBits(1) == 0) {
+ ++numZeroes;
+ }
+
+ unsigned x = br->getBits(numZeroes);
+
+ return x + (1u << numZeroes) - 1;
+}
+
+// Determine video dimensions from the sequence parameterset.
+static void FindDimensions(
+ const sp<ABuffer> seqParamSet, int32_t *width, int32_t *height) {
+ ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
+
+ unsigned profile_idc = br.getBits(8);
+ br.skipBits(16);
+ parseUE(&br); // seq_parameter_set_id
+
+ if (profile_idc == 100 || profile_idc == 110
+ || profile_idc == 122 || profile_idc == 144) {
+ TRESPASS();
+ }
+
+ parseUE(&br); // log2_max_frame_num_minus4
+ unsigned pic_order_cnt_type = parseUE(&br);
+
+ if (pic_order_cnt_type == 0) {
+ parseUE(&br); // log2_max_pic_order_cnt_lsb_minus4
+ } else if (pic_order_cnt_type == 1) {
+ br.getBits(1); // delta_pic_order_always_zero_flag
+ parseUE(&br); // offset_for_non_ref_pic
+ parseUE(&br); // offset_for_top_to_bottom_field
+
+ unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br);
+ for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
+ parseUE(&br); // offset_for_ref_frame
+ }
+ }
+
+ parseUE(&br); // num_ref_frames
+ br.getBits(1); // gaps_in_frame_num_value_allowed_flag
+
+ unsigned pic_width_in_mbs_minus1 = parseUE(&br);
+ unsigned pic_height_in_map_units_minus1 = parseUE(&br);
+ unsigned frame_mbs_only_flag = br.getBits(1);
+
+ *width = pic_width_in_mbs_minus1 * 16 + 16;
+
+ *height = (2 - frame_mbs_only_flag)
+ * (pic_height_in_map_units_minus1 * 16 + 16);
+}
+
+static sp<ABuffer> MakeAVCCodecSpecificData(
+ const sp<ABuffer> &buffer, int32_t *width, int32_t *height) {
+ const uint8_t *data = buffer->data();
+ size_t size = buffer->size();
+
+ sp<ABuffer> seqParamSet = FindNAL(data, size, 7, NULL);
+ if (seqParamSet == NULL) {
+ return NULL;
+ }
+
+ FindDimensions(seqParamSet, width, height);
+
+ size_t stopOffset;
+ sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
+ CHECK(picParamSet != NULL);
+
+ buffer->setRange(stopOffset, size - stopOffset);
+ LOG(INFO) << "buffer has " << buffer->size() << " bytes left.";
+
+ size_t csdSize =
+ 1 + 3 + 1 + 1
+ + 2 * 1 + seqParamSet->size()
+ + 1 + 2 * 1 + picParamSet->size();
+
+ sp<ABuffer> csd = new ABuffer(csdSize);
+ uint8_t *out = csd->data();
+
+ *out++ = 0x01; // configurationVersion
+ memcpy(out, seqParamSet->data() + 1, 3); // profile/level...
+ out += 3;
+ *out++ = (0x3f << 2) | 1; // lengthSize == 2 bytes
+ *out++ = 0xe0 | 1;
+
+ *out++ = seqParamSet->size() >> 8;
+ *out++ = seqParamSet->size() & 0xff;
+ memcpy(out, seqParamSet->data(), seqParamSet->size());
+ out += seqParamSet->size();
+
+ *out++ = 1;
+
+ *out++ = picParamSet->size() >> 8;
+ *out++ = picParamSet->size() & 0xff;
+ memcpy(out, picParamSet->data(), picParamSet->size());
+
+ return csd;
+}
+
+static bool getNextNALUnit(
+ const uint8_t **_data, size_t *_size,
+ const uint8_t **nalStart, size_t *nalSize) {
+ const uint8_t *data = *_data;
+ size_t size = *_size;
+
+ *nalStart = NULL;
+ *nalSize = 0;
+
+ if (size == 0) {
+ return false;
+ }
+
+ size_t offset = 0;
+ for (;;) {
+ CHECK_LT(offset + 2, size);
+
+ if (!memcmp("\x00\x00\x01", &data[offset], 3)) {
+ break;
+ }
+
+ CHECK_EQ((unsigned)data[offset], 0x00u);
+ ++offset;
+ }
+
+ offset += 3;
+ size_t startOffset = offset;
+
+ while (offset + 2 < size
+ && memcmp("\x00\x00\x00", &data[offset], 3)
+ && memcmp("\x00\x00\x01", &data[offset], 3)) {
+ ++offset;
+ }
+
+ if (offset + 2 >= size) {
+ *nalStart = &data[startOffset];
+ *nalSize = size - startOffset;
+
+ *_data = NULL;
+ *_size = 0;
+
+ return true;
+ }
+
+ size_t endOffset = offset;
+
+ while (offset + 2 < size && memcmp("\x00\x00\x01", &data[offset], 3)) {
+ CHECK_EQ((unsigned)data[offset], 0x00u);
+ ++offset;
+ }
+
+ CHECK_LT(offset + 2, size);
+
+ *nalStart = &data[startOffset];
+ *nalSize = endOffset - startOffset;
+
+ *_data = &data[offset];
+ *_size = size - offset;
+
+ return true;
+}
+
+sp<ABuffer> MakeCleanAVCData(const uint8_t *data, size_t size) {
+ const uint8_t *tmpData = data;
+ size_t tmpSize = size;
+
+ size_t totalSize = 0;
+ const uint8_t *nalStart;
+ size_t nalSize;
+ while (getNextNALUnit(&tmpData, &tmpSize, &nalStart, &nalSize)) {
+ totalSize += 4 + nalSize;
+ }
+
+ sp<ABuffer> buffer = new ABuffer(totalSize);
+ size_t offset = 0;
+ while (getNextNALUnit(&data, &size, &nalStart, &nalSize)) {
+ memcpy(buffer->data() + offset, "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + offset + 4, nalStart, nalSize);
+
+ offset += 4 + nalSize;
+ }
+
+ return buffer;
+}
+
+static sp<ABuffer> FindMPEG2ADTSConfig(
+ const sp<ABuffer> &buffer, int32_t *sampleRate, int32_t *channelCount) {
+ ABitReader br(buffer->data(), buffer->size());
+
+ CHECK_EQ(br.getBits(12), 0xfffu);
+ CHECK_EQ(br.getBits(1), 0u);
+ CHECK_EQ(br.getBits(2), 0u);
+ br.getBits(1); // protection_absent
+ unsigned profile = br.getBits(2);
+ LOG(INFO) << "profile = " << profile;
+ CHECK_NE(profile, 3u);
+ unsigned sampling_freq_index = br.getBits(4);
+ br.getBits(1); // private_bit
+ unsigned channel_configuration = br.getBits(3);
+ CHECK_NE(channel_configuration, 0u);
+
+ LOG(INFO) << "sampling_freq_index = " << sampling_freq_index;
+ LOG(INFO) << "channel_configuration = " << channel_configuration;
+
+ CHECK_LE(sampling_freq_index, 11u);
+ static const int32_t kSamplingFreq[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+ 16000, 12000, 11025, 8000
+ };
+ *sampleRate = kSamplingFreq[sampling_freq_index];
+
+ *channelCount = channel_configuration;
+
+ static const uint8_t kStaticESDS[] = {
+ 0x03, 22,
+ 0x00, 0x00, // ES_ID
+ 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+ 0x04, 17,
+ 0x40, // Audio ISO/IEC 14496-3
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x05, 2,
+ // AudioSpecificInfo follows
+
+ // oooo offf fccc c000
+ // o - audioObjectType
+ // f - samplingFreqIndex
+ // c - channelConfig
+ };
+ sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
+ memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
+
+ csd->data()[sizeof(kStaticESDS)] =
+ ((profile + 1) << 3) | (sampling_freq_index >> 1);
+
+ csd->data()[sizeof(kStaticESDS) + 1] =
+ ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
+
+ hexdump(csd->data(), csd->size());
+ return csd;
+}
+
+void ATSParser::Stream::onPayloadData(
+ unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
+ const uint8_t *data, size_t size) {
+ LOG(VERBOSE) << "onPayloadData mStreamType="
+ << StringPrintf("0x%02x", mStreamType);
+
+ sp<ABuffer> buffer;
+
+ if (mStreamType == 0x1b) {
+ buffer = MakeCleanAVCData(data, size);
+ } else {
+ // hexdump(data, size);
+
+ buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+ }
+
+ if (mSource == NULL) {
+ sp<MetaData> meta = new MetaData;
+
+ if (mStreamType == 0x1b) {
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+
+ int32_t width, height;
+ sp<ABuffer> csd = MakeAVCCodecSpecificData(buffer, &width, &height);
+
+ if (csd == NULL) {
+ return;
+ }
+
+ meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
+ meta->setInt32(kKeyWidth, width);
+ meta->setInt32(kKeyHeight, height);
+ } else {
+ CHECK_EQ(mStreamType, 0x0fu);
+
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ int32_t sampleRate, channelCount;
+ sp<ABuffer> csd =
+ FindMPEG2ADTSConfig(buffer, &sampleRate, &channelCount);
+
+ LOG(INFO) << "sampleRate = " << sampleRate;
+ LOG(INFO) << "channelCount = " << channelCount;
+
+ meta->setInt32(kKeySampleRate, sampleRate);
+ meta->setInt32(kKeyChannelCount, channelCount);
+
+ meta->setData(kKeyESDS, 0, csd->data(), csd->size());
+ }
+
+ LOG(INFO) << "created source!";
+ mSource = new AnotherPacketSource(meta);
+
+ // fall through
+ }
+
+ CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3);
+ buffer->meta()->setInt64("time", (PTS * 100) / 9);
+
+ if (mStreamType == 0x0f) {
+ // WHY???
+ buffer->setRange(7, buffer->size() - 7);
+ }
+
+ mSource->queueAccessUnit(buffer);
+}
+
+sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
+ if ((type == AVC_VIDEO && mStreamType == 0x1b)
+ || (type == MPEG2ADTS_AUDIO && mStreamType == 0x0f)) {
+ return mSource;
+ }
+
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ATSParser::ATSParser() {
+}
+
+ATSParser::~ATSParser() {
+}
+
+void ATSParser::feedTSPacket(const void *data, size_t size) {
+ CHECK_EQ(size, kTSPacketSize);
+
+ ABitReader br((const uint8_t *)data, kTSPacketSize);
+ parseTS(&br);
+}
+
+void ATSParser::parseProgramAssociationTable(ABitReader *br) {
+ unsigned table_id = br->getBits(8);
+ LOG(VERBOSE) << " table_id = " << table_id;
+ CHECK_EQ(table_id, 0x00u);
+
+ unsigned section_syntax_indictor = br->getBits(1);
+ LOG(VERBOSE) << " section_syntax_indictor = " << section_syntax_indictor;
+ CHECK_EQ(section_syntax_indictor, 1u);
+
+ CHECK_EQ(br->getBits(1), 0u);
+ LOG(VERBOSE) << " reserved = " << br->getBits(2);
+
+ unsigned section_length = br->getBits(12);
+ LOG(VERBOSE) << " section_length = " << section_length;
+ CHECK((section_length & 0xc00) == 0);
+
+ LOG(VERBOSE) << " transport_stream_id = " << br->getBits(16);
+ LOG(VERBOSE) << " reserved = " << br->getBits(2);
+ LOG(VERBOSE) << " version_number = " << br->getBits(5);
+ LOG(VERBOSE) << " current_next_indicator = " << br->getBits(1);
+ LOG(VERBOSE) << " section_number = " << br->getBits(8);
+ LOG(VERBOSE) << " last_section_number = " << br->getBits(8);
+
+ size_t numProgramBytes = (section_length - 5 /* header */ - 4 /* crc */);
+ CHECK_EQ((numProgramBytes % 4), 0u);
+
+ for (size_t i = 0; i < numProgramBytes / 4; ++i) {
+ unsigned program_number = br->getBits(16);
+ LOG(VERBOSE) << " program_number = " << program_number;
+
+ LOG(VERBOSE) << " reserved = " << br->getBits(3);
+
+ if (program_number == 0) {
+ LOG(VERBOSE) << " network_PID = "
+ << StringPrintf("0x%04x", br->getBits(13));
+ } else {
+ unsigned programMapPID = br->getBits(13);
+
+ LOG(VERBOSE) << " program_map_PID = "
+ << StringPrintf("0x%04x", programMapPID);
+
+ mPrograms.push(new Program(programMapPID));
+ }
+ }
+
+ LOG(VERBOSE) << " CRC = " << StringPrintf("0x%08x", br->getBits(32));
+}
+
+void ATSParser::parsePID(
+ ABitReader *br, unsigned PID,
+ unsigned payload_unit_start_indicator) {
+ if (PID == 0) {
+ if (payload_unit_start_indicator) {
+ unsigned skip = br->getBits(8);
+ br->skipBits(skip * 8);
+ }
+ parseProgramAssociationTable(br);
+ return;
+ }
+
+ bool handled = false;
+ for (size_t i = 0; i < mPrograms.size(); ++i) {
+ if (mPrograms.editItemAt(i)->parsePID(
+ PID, payload_unit_start_indicator, br)) {
+ handled = true;
+ break;
+ }
+ }
+
+ if (!handled) {
+ LOG(WARNING) << "PID " << StringPrintf("0x%04x", PID)
+ << " not handled.";
+ }
+}
+
+void ATSParser::parseAdaptationField(ABitReader *br) {
+ unsigned adaptation_field_length = br->getBits(8);
+ if (adaptation_field_length > 0) {
+ br->skipBits(adaptation_field_length * 8); // XXX
+ }
+}
+
+void ATSParser::parseTS(ABitReader *br) {
+ LOG(VERBOSE) << "---";
+
+ unsigned sync_byte = br->getBits(8);
+ CHECK_EQ(sync_byte, 0x47u);
+
+ LOG(VERBOSE) << "transport_error_indicator = " << br->getBits(1);
+
+ unsigned payload_unit_start_indicator = br->getBits(1);
+ LOG(VERBOSE) << "payload_unit_start_indicator = "
+ << payload_unit_start_indicator;
+
+ LOG(VERBOSE) << "transport_priority = " << br->getBits(1);
+
+ unsigned PID = br->getBits(13);
+ LOG(VERBOSE) << "PID = " << StringPrintf("0x%04x", PID);
+
+ LOG(VERBOSE) << "transport_scrambling_control = " << br->getBits(2);
+
+ unsigned adaptation_field_control = br->getBits(2);
+ LOG(VERBOSE) << "adaptation_field_control = " << adaptation_field_control;
+
+ LOG(VERBOSE) << "continuity_counter = " << br->getBits(4);
+
+ if (adaptation_field_control == 2 || adaptation_field_control == 3) {
+ parseAdaptationField(br);
+ }
+
+ if (adaptation_field_control == 1 || adaptation_field_control == 3) {
+ parsePID(br, PID, payload_unit_start_indicator);
+ }
+}
+
+sp<MediaSource> ATSParser::getSource(SourceType type) {
+ for (size_t i = 0; i < mPrograms.size(); ++i) {
+ sp<MediaSource> source = mPrograms.editItemAt(i)->getSource(type);
+
+ if (source != NULL) {
+ return source;
+ }
+ }
+
+ return NULL;
+}
+
+} // namespace android
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
new file mode 100644
index 0000000..1e22e7b
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -0,0 +1,68 @@
+/*
+ * 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 A_TS_PARSER_H_
+
+#define A_TS_PARSER_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Vector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABitReader;
+struct MediaSource;
+
+struct ATSParser : public RefBase {
+ ATSParser();
+
+ void feedTSPacket(const void *data, size_t size);
+
+ enum SourceType {
+ AVC_VIDEO,
+ MPEG2ADTS_AUDIO
+ };
+ sp<MediaSource> getSource(SourceType type);
+
+protected:
+ virtual ~ATSParser();
+
+private:
+ struct Program;
+ struct Stream;
+
+ Vector<sp<Program> > mPrograms;
+
+ void parseProgramAssociationTable(ABitReader *br);
+ void parseProgramMap(ABitReader *br);
+ void parsePES(ABitReader *br);
+
+ void parsePID(
+ ABitReader *br, unsigned PID,
+ unsigned payload_unit_start_indicator);
+
+ void parseAdaptationField(ABitReader *br);
+ void parseTS(ABitReader *br);
+
+ DISALLOW_EVIL_CONSTRUCTORS(ATSParser);
+};
+
+} // namespace android
+
+#endif // A_TS_PARSER_H_
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
new file mode 100644
index 0000000..b6772eb
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ ABitReader.cpp \
+ AnotherPacketSource.cpp \
+ ATSParser.cpp \
+ MPEG2TSExtractor.cpp \
+
+LOCAL_C_INCLUDES:= \
+ $(JNI_H_INCLUDE) \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+ $(TOP)/frameworks/base/media/libstagefright
+
+LOCAL_MODULE:= libstagefright_mpeg2ts
+
+ifeq ($(TARGET_ARCH),arm)
+ LOCAL_CFLAGS += -Wno-psabi
+endif
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
new file mode 100644
index 0000000..3d51177
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#include "AnotherPacketSource.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+AnotherPacketSource::AnotherPacketSource(const sp<MetaData> &meta)
+ : mFormat(meta),
+ mEOSResult(OK) {
+}
+
+AnotherPacketSource::~AnotherPacketSource() {
+}
+
+status_t AnotherPacketSource::start(MetaData *params) {
+ return OK;
+}
+
+status_t AnotherPacketSource::stop() {
+ return OK;
+}
+
+sp<MetaData> AnotherPacketSource::getFormat() {
+ return mFormat;
+}
+
+status_t AnotherPacketSource::read(
+ MediaBuffer **out, const ReadOptions *) {
+ *out = NULL;
+
+ Mutex::Autolock autoLock(mLock);
+ while (mEOSResult == OK && mBuffers.empty()) {
+ mCondition.wait(mLock);
+ }
+
+ if (!mBuffers.empty()) {
+ const sp<ABuffer> buffer = *mBuffers.begin();
+
+ uint64_t timeUs;
+ CHECK(buffer->meta()->findInt64(
+ "time", (int64_t *)&timeUs));
+
+ MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
+ mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+
+ // hexdump(buffer->data(), buffer->size());
+
+ memcpy(mediaBuffer->data(), buffer->data(), buffer->size());
+ *out = mediaBuffer;
+
+ mBuffers.erase(mBuffers.begin());
+ return OK;
+ }
+
+ return mEOSResult;
+}
+
+void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
+ int32_t damaged;
+ if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
+ // LOG(VERBOSE) << "discarding damaged AU";
+ return;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+ mBuffers.push_back(buffer);
+ mCondition.signal();
+}
+
+void AnotherPacketSource::signalEOS(status_t result) {
+ CHECK(result != OK);
+
+ Mutex::Autolock autoLock(mLock);
+ mEOSResult = result;
+ mCondition.signal();
+}
+
+bool AnotherPacketSource::hasBufferAvailable(status_t *finalResult) {
+ Mutex::Autolock autoLock(mLock);
+ if (!mBuffers.empty()) {
+ return true;
+ }
+
+ *finalResult = mEOSResult;
+ return false;
+}
+
+} // namespace android
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
new file mode 100644
index 0000000..ce83d21
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -0,0 +1,62 @@
+/*
+ * 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 ANOTHER_PACKET_SOURCE_H_
+
+#define ANOTHER_PACKET_SOURCE_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct AnotherPacketSource : public MediaSource {
+ AnotherPacketSource(const sp<MetaData> &meta);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+ bool hasBufferAvailable(status_t *finalResult);
+
+ void queueAccessUnit(const sp<ABuffer> &buffer);
+ void signalEOS(status_t result);
+
+protected:
+ virtual ~AnotherPacketSource();
+
+private:
+ Mutex mLock;
+ Condition mCondition;
+
+ sp<MetaData> mFormat;
+ List<sp<ABuffer> > mBuffers;
+ status_t mEOSResult;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AnotherPacketSource);
+};
+
+
+} // namespace android
+
+#endif // ANOTHER_PACKET_SOURCE_H_
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
new file mode 100644
index 0000000..b798273
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MPEG2TSExtractor"
+#include <utils/Log.h>
+
+#include "include/MPEG2TSExtractor.h"
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/String8.h>
+
+#include "AnotherPacketSource.h"
+#include "ATSParser.h"
+
+namespace android {
+
+struct MPEG2TSSource : public MediaSource {
+ MPEG2TSSource(
+ const sp<MPEG2TSExtractor> &extractor,
+ const sp<AnotherPacketSource> &impl);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+private:
+ sp<MPEG2TSExtractor> mExtractor;
+ sp<AnotherPacketSource> mImpl;
+
+ DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSSource);
+};
+
+MPEG2TSSource::MPEG2TSSource(
+ const sp<MPEG2TSExtractor> &extractor,
+ const sp<AnotherPacketSource> &impl)
+ : mExtractor(extractor),
+ mImpl(impl) {
+}
+
+status_t MPEG2TSSource::start(MetaData *params) {
+ return mImpl->start(params);
+}
+
+status_t MPEG2TSSource::stop() {
+ return mImpl->stop();
+}
+
+sp<MetaData> MPEG2TSSource::getFormat() {
+ return mImpl->getFormat();
+}
+
+status_t MPEG2TSSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ status_t finalResult;
+ while (!mImpl->hasBufferAvailable(&finalResult)) {
+ if (finalResult != OK) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ status_t err = mExtractor->feedMore();
+ if (err != OK) {
+ mImpl->signalEOS(err);
+ }
+ }
+
+ return mImpl->read(out, options);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG2TSExtractor::MPEG2TSExtractor(const sp<DataSource> &source)
+ : mDataSource(source),
+ mParser(new ATSParser),
+ mOffset(0) {
+ init();
+}
+
+size_t MPEG2TSExtractor::countTracks() {
+ return mSourceImpls.size();
+}
+
+sp<MediaSource> MPEG2TSExtractor::getTrack(size_t index) {
+ if (index >= mSourceImpls.size()) {
+ return NULL;
+ }
+
+ return new MPEG2TSSource(this, mSourceImpls.editItemAt(index));
+}
+
+sp<MetaData> MPEG2TSExtractor::getTrackMetaData(
+ size_t index, uint32_t flags) {
+ return index < mSourceImpls.size()
+ ? mSourceImpls.editItemAt(index)->getFormat() : NULL;
+}
+
+sp<MetaData> MPEG2TSExtractor::getMetaData() {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
+
+ return meta;
+}
+
+void MPEG2TSExtractor::init() {
+ bool haveAudio = false;
+ bool haveVideo = false;
+
+ while (feedMore() == OK) {
+ ATSParser::SourceType type;
+ if (haveAudio && haveVideo) {
+ break;
+ }
+ if (haveVideo) {
+ type = ATSParser::MPEG2ADTS_AUDIO;
+ } else {
+ type = ATSParser::AVC_VIDEO;
+ }
+ sp<AnotherPacketSource> impl =
+ (AnotherPacketSource *)mParser->getSource(type).get();
+
+ if (impl != NULL) {
+ if (type == ATSParser::MPEG2ADTS_AUDIO) {
+ haveAudio = true;
+ } else {
+ haveVideo = true;
+ }
+ mSourceImpls.push(impl);
+ }
+ }
+
+ LOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo);
+}
+
+status_t MPEG2TSExtractor::feedMore() {
+ Mutex::Autolock autoLock(mLock);
+
+ static const size_t kTSPacketSize = 188;
+
+ uint8_t packet[kTSPacketSize];
+ ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
+
+ if (n < (ssize_t)kTSPacketSize) {
+ return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
+ }
+
+ mOffset += kTSPacketSize;
+ mParser->feedTSPacket(packet, kTSPacketSize);
+
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool SniffMPEG2TS(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+ char header;
+ if (source->readAt(0, &header, 1) != 1 || header != 0x47) {
+ return false;
+ }
+
+ *confidence = 0.05f;
+ mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
+
+ return true;
+}
+
+} // namespace android
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..1a3063c4 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..c3e24158
--- /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..027bed4a 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..5eb0d683
--- /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/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 62cf7076..4a0df59 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -280,7 +280,9 @@
mTotalMemory = ((long)mDataFileStats.getBlockCount() *
mDataFileStats.getBlockSize())/100L;
mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
+ mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
+ mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
checkMemory(true);
}
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/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 599023c..79da3cf 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -3648,21 +3648,19 @@
installedNativeLibraries = true;
+ // Always extract the shared library
String sharedLibraryFilePath = sharedLibraryDir.getPath() +
File.separator + libFileName;
File sharedLibraryFile = new File(sharedLibraryFilePath);
- if (! sharedLibraryFile.exists() ||
- sharedLibraryFile.length() != entry.getSize() ||
- sharedLibraryFile.lastModified() != entry.getTime()) {
- if (Config.LOGD) {
- Log.d(TAG, "Caching shared lib " + entry.getName());
- }
- if (mInstaller == null) {
- sharedLibraryDir.mkdir();
- }
- cacheNativeBinaryLI(pkg, zipFile, entry, sharedLibraryDir,
- sharedLibraryFile);
+
+ if (Config.LOGD) {
+ Log.d(TAG, "Caching shared lib " + entry.getName());
}
+ if (mInstaller == null) {
+ sharedLibraryDir.mkdir();
+ }
+ cacheNativeBinaryLI(pkg, zipFile, entry, sharedLibraryDir,
+ sharedLibraryFile);
}
if (!hasNativeLibraries)
return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
@@ -3704,18 +3702,16 @@
String installGdbServerPath = installGdbServerDir.getPath() +
"/" + GDBSERVER;
File installGdbServerFile = new File(installGdbServerPath);
- if (! installGdbServerFile.exists() ||
- installGdbServerFile.length() != entry.getSize() ||
- installGdbServerFile.lastModified() != entry.getTime()) {
- if (Config.LOGD) {
- Log.d(TAG, "Caching gdbserver " + entry.getName());
- }
- if (mInstaller == null) {
- installGdbServerDir.mkdir();
- }
- cacheNativeBinaryLI(pkg, zipFile, entry, installGdbServerDir,
- installGdbServerFile);
+
+ if (Config.LOGD) {
+ Log.d(TAG, "Caching gdbserver " + entry.getName());
}
+ if (mInstaller == null) {
+ installGdbServerDir.mkdir();
+ }
+ cacheNativeBinaryLI(pkg, zipFile, entry, installGdbServerDir,
+ installGdbServerFile);
+
return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
}
return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
@@ -3729,6 +3725,16 @@
// one if ro.product.cpu.abi2 is defined.
//
private int cachePackageSharedLibsLI(PackageParser.Package pkg, File scanFile) {
+ // Remove all native binaries from a directory. This is used when upgrading
+ // a package: in case the new .apk doesn't contain a native binary that was
+ // in the old one (and thus installed), we need to remove it from
+ // /data/data/<appname>/lib
+ //
+ // The simplest way to do that is to remove all files in this directory,
+ // since it is owned by "system", applications are not supposed to write
+ // anything there.
+ removeNativeBinariesLI(pkg);
+
String cpuAbi = Build.CPU_ABI;
try {
int result = cachePackageSharedLibsForAbiLI(pkg, scanFile, cpuAbi);
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/Watchdog.java b/services/java/com/android/server/Watchdog.java
index 5eaadbc..d4133f3 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -39,6 +39,8 @@
import android.util.Slog;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
@@ -51,6 +53,9 @@
// Set this to true to use debug default values.
static final boolean DB = false;
+ // Set this to true to have the watchdog record kernel thread stacks when it fires
+ static final boolean RECORD_KERNEL_THREADS = true;
+
static final int MONITOR = 2718;
static final int GLOBAL_PSS = 2719;
@@ -850,6 +855,11 @@
// The system's been hanging for a minute, another second or two won't hurt much.
SystemClock.sleep(2000);
+ // Pull our own kernel thread stacks as well if we're configured for that
+ if (RECORD_KERNEL_THREADS) {
+ dumpKernelStackTraces();
+ }
+
mActivity.addErrorToDropBox("watchdog", null, null, null, name, null, stack, null);
// Only kill the process if the debugger is not attached.
@@ -864,4 +874,16 @@
waitedHalf = false;
}
}
+
+ private File dumpKernelStackTraces() {
+ String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
+ if (tracesPath == null || tracesPath.length() == 0) {
+ return null;
+ }
+
+ native_dumpKernelStacks(tracesPath);
+ return new File(tracesPath);
+ }
+
+ private native void native_dumpKernelStacks(String tracesPath);
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8857c5f..c61f90e 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import com.android.internal.R;
+import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
@@ -39,10 +41,12 @@
import android.app.IActivityWatcher;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
+import android.app.INotificationManager;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
import android.app.Instrumentation;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ResultInfo;
import android.app.Service;
@@ -69,6 +73,7 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
@@ -473,7 +478,8 @@
* to become visible before completing whatever operation they are
* supposed to do.
*/
- final ArrayList mWaitingVisibleActivities = new ArrayList();
+ final ArrayList<HistoryRecord> mWaitingVisibleActivities
+ = new ArrayList<HistoryRecord>();
/**
* List of activities that are ready to be stopped, but waiting
@@ -501,7 +507,8 @@
* for the previous activity to settle down before doing so. It contains
* HistoryRecord objects.
*/
- final ArrayList mFinishingActivities = new ArrayList();
+ final ArrayList<HistoryRecord> mFinishingActivities
+ = new ArrayList<HistoryRecord>();
/**
* All of the applications we currently have running organized by name.
@@ -513,6 +520,11 @@
= new ProcessMap<ProcessRecord>();
/**
+ * The currently running heavy-weight process, if any.
+ */
+ ProcessRecord mHeavyWeightProcess = null;
+
+ /**
* The last time that various processes have crashed.
*/
final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<Long>();
@@ -726,21 +738,24 @@
* that a single provider may be published under multiple names, so
* there may be multiple entries here for a single one in mProvidersByClass.
*/
- final HashMap mProvidersByName = new HashMap();
+ final HashMap<String, ContentProviderRecord> mProvidersByName
+ = new HashMap<String, ContentProviderRecord>();
/**
* All of the currently running global content providers. Keys are a
* string containing the provider's implementation class and values are a
* ContentProviderRecord object containing the data about it.
*/
- final HashMap mProvidersByClass = new HashMap();
+ final HashMap<String, ContentProviderRecord> mProvidersByClass
+ = new HashMap<String, ContentProviderRecord>();
/**
* List of content providers who have clients waiting for them. The
* application is currently being launched and the provider will be
* removed from this list once it is published.
*/
- final ArrayList mLaunchingProviders = new ArrayList();
+ final ArrayList<ContentProviderRecord> mLaunchingProviders
+ = new ArrayList<ContentProviderRecord>();
/**
* Global set of specific Uri permissions that have been granted.
@@ -1006,6 +1021,8 @@
static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
static final int KILL_APPLICATION_MSG = 22;
static final int FINALIZE_PENDING_INTENT_MSG = 23;
+ static final int POST_HEAVY_NOTIFICATION_MSG = 24;
+ static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
AlertDialog mUidAlert;
@@ -1235,6 +1252,62 @@
case FINALIZE_PENDING_INTENT_MSG: {
((PendingIntentRecord)msg.obj).completeFinalize();
} break;
+ case POST_HEAVY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+
+ HistoryRecord root = (HistoryRecord)msg.obj;
+ ProcessRecord process = root.app;
+ if (process == null) {
+ return;
+ }
+
+ try {
+ Context context = mContext.createPackageContext(process.info.packageName, 0);
+ String text = mContext.getString(R.string.heavy_weight_notification,
+ context.getApplicationInfo().loadLabel(context.getPackageManager()));
+ Notification notification = new Notification();
+ notification.icon = com.android.internal.R.drawable.stat_sys_adb; //context.getApplicationInfo().icon;
+ notification.when = 0;
+ notification.flags = Notification.FLAG_ONGOING_EVENT;
+ notification.tickerText = text;
+ notification.defaults = 0; // please be quiet
+ notification.sound = null;
+ notification.vibrate = null;
+ notification.setLatestEventInfo(context, text,
+ mContext.getText(R.string.heavy_weight_notification_detail),
+ PendingIntent.getActivity(mContext, 0, root.intent,
+ PendingIntent.FLAG_CANCEL_CURRENT));
+
+ try {
+ int[] outId = new int[1];
+ inm.enqueueNotification("android", R.string.heavy_weight_notification,
+ notification, outId);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error showing notification for heavy-weight app", e);
+ } catch (RemoteException e) {
+ }
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Unable to create context for heavy notification", e);
+ }
+ } break;
+ case CANCEL_HEAVY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotification("android",
+ R.string.heavy_weight_notification);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ } break;
}
}
};
@@ -1822,6 +1895,24 @@
System.identityHashCode(r),
r.info, r.icicle, results, newIntents, !andResume,
isNextTransitionForward());
+
+ if ((app.info.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+ // This may be a heavy-weight process! Note that the package
+ // manager will ensure that only activity can run in the main
+ // process of the .apk, which is the only thing that will be
+ // considered heavy-weight.
+ if (app.processName.equals(app.info.packageName)) {
+ if (mHeavyWeightProcess != null && mHeavyWeightProcess != app) {
+ Log.w(TAG, "Starting new heavy weight process " + app
+ + " when already running " + mHeavyWeightProcess);
+ }
+ mHeavyWeightProcess = app;
+ Message msg = mHandler.obtainMessage(POST_HEAVY_NOTIFICATION_MSG);
+ msg.obj = r;
+ mHandler.sendMessage(msg);
+ }
+ }
+
} catch (RemoteException e) {
if (r.launchFailed) {
// This is the second time we failed -- finish activity
@@ -3678,7 +3769,7 @@
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- final boolean componentSpecified = intent.getComponent() != null;
+ boolean componentSpecified = intent.getComponent() != null;
// Don't modify the client's object!
intent = new Intent(intent);
@@ -3728,6 +3819,74 @@
final long origId = Binder.clearCallingIdentity();
+ if (aInfo != null &&
+ (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+ // This may be a heavy-weight process! Check to see if we already
+ // have another, different heavy-weight process running.
+ if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
+ if (mHeavyWeightProcess != null &&
+ (mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
+ !mHeavyWeightProcess.processName.equals(aInfo.processName))) {
+ int realCallingPid = callingPid;
+ int realCallingUid = callingUid;
+ if (caller != null) {
+ ProcessRecord callerApp = getRecordForAppLocked(caller);
+ if (callerApp != null) {
+ realCallingPid = callerApp.pid;
+ realCallingUid = callerApp.info.uid;
+ } else {
+ Slog.w(TAG, "Unable to find app for caller " + caller
+ + " (pid=" + realCallingPid + ") when starting: "
+ + intent.toString());
+ return START_PERMISSION_DENIED;
+ }
+ }
+
+ IIntentSender target = getIntentSenderLocked(
+ IActivityManager.INTENT_SENDER_ACTIVITY, "android",
+ realCallingUid, null, null, 0, intent,
+ resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
+ | PendingIntent.FLAG_ONE_SHOT);
+
+ Intent newIntent = new Intent();
+ if (requestCode >= 0) {
+ // Caller is requesting a result.
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
+ new IntentSender(target));
+ if (mHeavyWeightProcess.activities.size() > 0) {
+ HistoryRecord hist = mHeavyWeightProcess.activities.get(0);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
+ hist.packageName);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
+ hist.task.taskId);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
+ aInfo.packageName);
+ newIntent.setFlags(intent.getFlags());
+ newIntent.setClassName("android",
+ HeavyWeightSwitcherActivity.class.getName());
+ intent = newIntent;
+ resolvedType = null;
+ caller = null;
+ callingUid = Binder.getCallingUid();
+ callingPid = Binder.getCallingPid();
+ componentSpecified = true;
+ try {
+ ResolveInfo rInfo =
+ ActivityThread.getPackageManager().resolveIntent(
+ intent, null,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | STOCK_PM_FLAGS);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ } catch (RemoteException e) {
+ aInfo = null;
+ }
+ }
+ }
+ }
+
int res = startActivityLocked(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, aInfo,
resultTo, resultWho, requestCode, callingPid, callingUid,
@@ -4316,6 +4475,40 @@
}
}
+ public final void finishHeavyWeightApp() {
+ if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: finishHeavyWeightApp() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ synchronized(this) {
+ if (mHeavyWeightProcess == null) {
+ return;
+ }
+
+ ArrayList<HistoryRecord> activities = new ArrayList<HistoryRecord>(
+ mHeavyWeightProcess.activities);
+ for (int i=0; i<activities.size(); i++) {
+ HistoryRecord r = activities.get(i);
+ if (!r.finishing) {
+ int index = indexOfTokenLocked(r);
+ if (index >= 0) {
+ finishActivityLocked(r, index, Activity.RESULT_CANCELED,
+ null, "finish-heavy");
+ }
+ }
+ }
+
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
+ }
+
void sendActivityResultLocked(int callingUid, HistoryRecord r,
String resultWho, int requestCode, int resultCode, Intent data) {
@@ -4513,6 +4706,10 @@
if (idx >= 0) {
r.app.activities.remove(idx);
}
+ if (mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
if (r.persistent) {
decPersistentCountLocked(r.app);
}
@@ -5375,6 +5572,10 @@
+ "/" + uid + ")");
mProcessNames.remove(name, uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
boolean needRestart = false;
if (app.pid > 0 && app.pid != MY_PID) {
int pid = app.pid;
@@ -5416,6 +5617,10 @@
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.info.uid,
app.processName);
mProcessNames.remove(app.processName, app.info.uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
// Take care of any launching providers waiting for this process.
checkAppInLaunchingProvidersLocked(app, true);
// Take care of any services that are waiting for the process.
@@ -6101,57 +6306,66 @@
throw new SecurityException(msg);
}
}
+
+ return getIntentSenderLocked(type, packageName, callingUid,
+ token, resultWho, requestCode, intent, resolvedType, flags);
+
} catch (RemoteException e) {
throw new SecurityException(e);
}
- HistoryRecord activity = null;
- if (type == INTENT_SENDER_ACTIVITY_RESULT) {
- int index = indexOfTokenLocked(token);
- if (index < 0) {
- return null;
- }
- activity = (HistoryRecord)mHistory.get(index);
- if (activity.finishing) {
- return null;
- }
+ }
+ }
+
+ IIntentSender getIntentSenderLocked(int type,
+ String packageName, int callingUid, IBinder token, String resultWho,
+ int requestCode, Intent intent, String resolvedType, int flags) {
+ HistoryRecord activity = null;
+ if (type == INTENT_SENDER_ACTIVITY_RESULT) {
+ int index = indexOfTokenLocked(token);
+ if (index < 0) {
+ return null;
}
-
- final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
- final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
- final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
- flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
- |PendingIntent.FLAG_UPDATE_CURRENT);
-
- PendingIntentRecord.Key key = new PendingIntentRecord.Key(
- type, packageName, activity, resultWho,
- requestCode, intent, resolvedType, flags);
- WeakReference<PendingIntentRecord> ref;
- ref = mIntentSenderRecords.get(key);
- PendingIntentRecord rec = ref != null ? ref.get() : null;
- if (rec != null) {
- if (!cancelCurrent) {
- if (updateCurrent) {
- rec.key.requestIntent.replaceExtras(intent);
- }
- return rec;
- }
- rec.canceled = true;
- mIntentSenderRecords.remove(key);
+ activity = (HistoryRecord)mHistory.get(index);
+ if (activity.finishing) {
+ return null;
}
- if (noCreate) {
+ }
+
+ final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
+ final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
+ final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
+ flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
+ |PendingIntent.FLAG_UPDATE_CURRENT);
+
+ PendingIntentRecord.Key key = new PendingIntentRecord.Key(
+ type, packageName, activity, resultWho,
+ requestCode, intent, resolvedType, flags);
+ WeakReference<PendingIntentRecord> ref;
+ ref = mIntentSenderRecords.get(key);
+ PendingIntentRecord rec = ref != null ? ref.get() : null;
+ if (rec != null) {
+ if (!cancelCurrent) {
+ if (updateCurrent) {
+ rec.key.requestIntent.replaceExtras(intent);
+ }
return rec;
}
- rec = new PendingIntentRecord(this, key, callingUid);
- mIntentSenderRecords.put(key, rec.ref);
- if (type == INTENT_SENDER_ACTIVITY_RESULT) {
- if (activity.pendingResults == null) {
- activity.pendingResults
- = new HashSet<WeakReference<PendingIntentRecord>>();
- }
- activity.pendingResults.add(rec.ref);
- }
+ rec.canceled = true;
+ mIntentSenderRecords.remove(key);
+ }
+ if (noCreate) {
return rec;
}
+ rec = new PendingIntentRecord(this, key, callingUid);
+ mIntentSenderRecords.put(key, rec.ref);
+ if (type == INTENT_SENDER_ACTIVITY_RESULT) {
+ if (activity.pendingResults == null) {
+ activity.pendingResults
+ = new HashSet<WeakReference<PendingIntentRecord>>();
+ }
+ activity.pendingResults.add(rec.ref);
+ }
+ return rec;
}
public void cancelIntentSender(IIntentSender sender) {
@@ -6450,8 +6664,7 @@
String name = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6648,8 +6861,7 @@
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProvidersByName.get(authority);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6743,8 +6955,7 @@
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProvidersByName.get(authority);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -7752,8 +7963,7 @@
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
- ContentProviderRecord cpr =
- (ContentProviderRecord)mProvidersByClass.get(cpi.name);
+ ContentProviderRecord cpr = mProvidersByClass.get(cpi.name);
if (cpr == null) {
cpr = new ContentProviderRecord(cpi, app.info);
mProvidersByClass.put(cpi.name, cpr);
@@ -7828,7 +8038,7 @@
}
// First check if this content provider has been published...
- cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ cpr = mProvidersByName.get(name);
if (cpr != null) {
cpi = cpr.info;
if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
@@ -7909,7 +8119,7 @@
"Attempt to launch content provider before system ready");
}
- cpr = (ContentProviderRecord)mProvidersByClass.get(cpi.name);
+ cpr = mProvidersByClass.get(cpi.name);
final boolean firstClass = cpr == null;
if (firstClass) {
try {
@@ -8043,7 +8253,7 @@
*/
public void removeContentProvider(IApplicationThread caller, String name) {
synchronized (this) {
- ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if(cpr == null) {
// remove from mProvidersByClass
if (DEBUG_PROVIDER) Slog.v(TAG, name +
@@ -8057,8 +8267,7 @@
" when removing content provider " + name);
}
//update content provider record entry info
- ContentProviderRecord localCpr = (ContentProviderRecord)
- mProvidersByClass.get(cpr.info.name);
+ ContentProviderRecord localCpr = mProvidersByClass.get(cpr.info.name);
if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
+ r.info.processName + " from process "
+ localCpr.appInfo.processName);
@@ -8082,7 +8291,7 @@
private void removeContentProviderExternal(String name) {
synchronized (this) {
- ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if(cpr == null) {
//remove from mProvidersByClass
if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -8090,7 +8299,7 @@
}
//update content provider record entry info
- ContentProviderRecord localCpr = (ContentProviderRecord) mProvidersByClass.get(cpr.info.name);
+ ContentProviderRecord localCpr = mProvidersByClass.get(cpr.info.name);
localCpr.externals--;
if (localCpr.externals < 0) {
Slog.e(TAG, "Externals < 0 for content provider " + localCpr);
@@ -8122,8 +8331,7 @@
if (src == null || src.info == null || src.provider == null) {
continue;
}
- ContentProviderRecord dst =
- (ContentProviderRecord)r.pubProviders.get(src.info.name);
+ ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
mProvidersByClass.put(dst.info.name, dst);
String names[] = dst.info.authority.split(";");
@@ -9038,9 +9246,9 @@
if (app.services.size() != 0) {
// Any services running in the application need to be placed
// back in the pending list.
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord sr = (ServiceRecord)it.next();
+ ServiceRecord sr = it.next();
sr.crashCount++;
}
}
@@ -9485,6 +9693,8 @@
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
} else if (adj >= VISIBLE_APP_ADJ) {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+ } else if (app == mHeavyWeightProcess) {
+ currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_HEAVY_WEIGHT;
} else {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
}
@@ -9860,6 +10070,9 @@
pw.println(" ");
pw.println(" mHomeProcess: " + mHomeProcess);
+ if (mHeavyWeightProcess != null) {
+ pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
+ }
pw.println(" mConfiguration: " + mConfiguration);
pw.println(" mConfigWillChange: " + mConfigWillChange);
pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
@@ -10121,10 +10334,11 @@
if (mProvidersByClass.size() > 0) {
if (needSep) pw.println(" ");
pw.println(" Published content providers (by class):");
- Iterator it = mProvidersByClass.entrySet().iterator();
+ Iterator<Map.Entry<String, ContentProviderRecord>> it
+ = mProvidersByClass.entrySet().iterator();
while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- ContentProviderRecord r = (ContentProviderRecord)e.getValue();
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
pw.print(" * "); pw.println(r);
r.dump(pw, " ");
}
@@ -10134,10 +10348,11 @@
if (mProvidersByName.size() > 0) {
pw.println(" ");
pw.println(" Authority to provider mappings:");
- Iterator it = mProvidersByName.entrySet().iterator();
+ Iterator<Map.Entry<String, ContentProviderRecord>> it
+ = mProvidersByName.entrySet().iterator();
while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- ContentProviderRecord r = (ContentProviderRecord)e.getValue();
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
pw.print(" "); pw.print(e.getKey()); pw.print(": ");
pw.println(r);
}
@@ -10370,9 +10585,9 @@
// XXX we are letting the client link to the service for
// death notifications.
if (app.services.size() > 0) {
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord r = (ServiceRecord)it.next();
+ ServiceRecord r = it.next();
if (r.connections.size() > 0) {
Iterator<ConnectionRecord> jt
= r.connections.values().iterator();
@@ -10407,9 +10622,9 @@
if (app.services.size() != 0) {
// Any services running in the application need to be placed
// back in the pending list.
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord sr = (ServiceRecord)it.next();
+ ServiceRecord sr = it.next();
synchronized (sr.stats.getBatteryStats()) {
sr.stats.stopLaunchedLocked();
}
@@ -10548,9 +10763,9 @@
// Remove published content providers.
if (!app.pubProviders.isEmpty()) {
- Iterator it = app.pubProviders.values().iterator();
+ Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
while (it.hasNext()) {
- ContentProviderRecord cpr = (ContentProviderRecord)it.next();
+ ContentProviderRecord cpr = it.next();
cpr.provider = null;
cpr.app = null;
@@ -10642,6 +10857,10 @@
if (DEBUG_PROCESSES) Slog.v(TAG,
"Removing non-persistent process during cleanup: " + app);
mProcessNames.remove(app.processName, app.info.uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
@@ -10683,8 +10902,7 @@
int NL = mLaunchingProviders.size();
boolean restart = false;
for (int i=0; i<NL; i++) {
- ContentProviderRecord cpr = (ContentProviderRecord)
- mLaunchingProviders.get(i);
+ ContentProviderRecord cpr = mLaunchingProviders.get(i);
if (cpr.launchingApp == app) {
if (!alwaysBad && !app.bad) {
restart = true;
@@ -11601,7 +11819,7 @@
public void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
boolean anyForeground = false;
- for (ServiceRecord sr : (HashSet<ServiceRecord>)proc.services) {
+ for (ServiceRecord sr : proc.services) {
if (sr.isForeground) {
anyForeground = true;
break;
@@ -13824,6 +14042,11 @@
adj = FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "instrumentation";
+ } else if (app == mHeavyWeightProcess) {
+ // We don't want to kill the current heavy-weight process.
+ adj = FOREGROUND_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.adjType = "heavy";
} else if (app.persistentActivities > 0) {
// Special persistent activities... shouldn't be used these days.
adj = FOREGROUND_APP_ADJ;
@@ -13867,7 +14090,7 @@
app.adjType = "bg-activities";
N = app.activities.size();
for (int j=0; j<N; j++) {
- if (((HistoryRecord)app.activities.get(j)).visible) {
+ if (app.activities.get(j).visible) {
// This app has a visible activity!
app.hidden = false;
adj = VISIBLE_APP_ADJ;
@@ -13910,9 +14133,9 @@
final long now = SystemClock.uptimeMillis();
// This process is more important if the top activity is
// bound to the service.
- Iterator jt = app.services.iterator();
+ Iterator<ServiceRecord> jt = app.services.iterator();
while (jt.hasNext() && adj > FOREGROUND_APP_ADJ) {
- ServiceRecord s = (ServiceRecord)jt.next();
+ ServiceRecord s = jt.next();
if (s.startRequested) {
if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) {
// This service has seen some activity within
@@ -13965,7 +14188,7 @@
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_SERVICE_IN_USE;
app.adjSource = cr.binding.client;
- app.adjTarget = s.serviceInfo.name;
+ app.adjTarget = s.name;
}
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
@@ -13987,7 +14210,7 @@
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_SERVICE_IN_USE;
app.adjSource = a;
- app.adjTarget = s.serviceInfo.name;
+ app.adjTarget = s.name;
}
}
}
@@ -14007,10 +14230,10 @@
if (app.pubProviders.size() != 0 && (adj > FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- Iterator jt = app.pubProviders.values().iterator();
+ Iterator<ContentProviderRecord> jt = app.pubProviders.values().iterator();
while (jt.hasNext() && (adj > FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- ContentProviderRecord cpr = (ContentProviderRecord)jt.next();
+ ContentProviderRecord cpr = jt.next();
if (cpr.clients.size() != 0) {
Iterator<ProcessRecord> kt = cpr.clients.iterator();
while (kt.hasNext() && adj > FOREGROUND_APP_ADJ) {
@@ -14039,7 +14262,7 @@
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_PROVIDER_IN_USE;
app.adjSource = client;
- app.adjTarget = cpr.info.name;
+ app.adjTarget = cpr.name;
}
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
schedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -14055,7 +14278,7 @@
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.hidden = false;
app.adjType = "provider";
- app.adjTarget = cpr.info.name;
+ app.adjTarget = cpr.name;
}
}
}
@@ -14491,7 +14714,7 @@
if (Config.LOGV) Slog.v(
TAG, "Looking to quit " + app.processName);
for (j=0; j<NUMA && canQuit; j++) {
- HistoryRecord r = (HistoryRecord)app.activities.get(j);
+ HistoryRecord r = app.activities.get(j);
if (Config.LOGV) Slog.v(
TAG, " " + r.intent.getComponent().flattenToShortString()
+ ": frozen=" + r.haveState + ", visible=" + r.visible);
@@ -14501,7 +14724,7 @@
if (canQuit) {
// Finish all of the activities, and then the app itself.
for (j=0; j<NUMA; j++) {
- HistoryRecord r = (HistoryRecord)app.activities.get(j);
+ HistoryRecord r = app.activities.get(j);
if (!r.finishing) {
destroyActivityLocked(r, false);
}
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index c764635..44c9742 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.app.IActivityManager.ContentProviderHolder;
+import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo;
import android.os.Process;
@@ -29,6 +30,7 @@
final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
final int uid;
final ApplicationInfo appInfo;
+ final ComponentName name;
int externals; // number of non-framework processes supported by this provider
ProcessRecord app; // if non-null, hosting application
ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
@@ -38,6 +40,7 @@
super(_info);
uid = ai.uid;
appInfo = ai;
+ name = new ComponentName(_info.packageName, _info.name);
noReleaseNeeded = uid == 0 || uid == Process.SYSTEM_UID;
}
@@ -45,6 +48,7 @@
super(cpr.info);
uid = cpr.uid;
appInfo = cpr.appInfo;
+ name = cpr.name;
noReleaseNeeded = cpr.noReleaseNeeded;
}
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index f49a182..9dda1df 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -28,7 +28,6 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.PrintWriterPrinter;
import java.io.PrintWriter;
@@ -45,7 +44,7 @@
final ApplicationInfo info; // all about the first app in the process
final String processName; // name of the process
// List of packages running in the process
- final HashSet<String> pkgList = new HashSet();
+ final HashSet<String> pkgList = new HashSet<String>();
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
// are in the process of launching the app)
@@ -87,9 +86,9 @@
Object adjTarget; // Debugging: target component impacting oom_adj.
// contains HistoryRecord objects
- final ArrayList activities = new ArrayList();
+ final ArrayList<HistoryRecord> activities = new ArrayList<HistoryRecord>();
// all ServiceRecord running in this process
- final HashSet services = new HashSet();
+ final HashSet<ServiceRecord> services = new HashSet<ServiceRecord>();
// services that are currently executing code (need to remain foreground).
final HashSet<ServiceRecord> executingServices
= new HashSet<ServiceRecord>();
@@ -99,7 +98,8 @@
// all IIntentReceivers that are registered from this process.
final HashSet<ReceiverList> receivers = new HashSet<ReceiverList>();
// class (String) -> ContentProviderRecord
- final HashMap pubProviders = new HashMap();
+ final HashMap<String, ContentProviderRecord> pubProviders
+ = new HashMap<String, ContentProviderRecord>();
// All ContentProviderRecord process is using
final HashMap<ContentProviderRecord, Integer> conProviders
= new HashMap<ContentProviderRecord, Integer>();
@@ -128,7 +128,6 @@
ComponentName errorReportReceiver;
void dump(PrintWriter pw, String prefix) {
- long now = SystemClock.uptimeMillis();
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
}
@@ -249,7 +248,7 @@
public boolean isInterestingToUserLocked() {
final int size = activities.size();
for (int i = 0 ; i < size ; i++) {
- HistoryRecord r = (HistoryRecord) activities.get(i);
+ HistoryRecord r = activities.get(i);
if (r.isInterestingToUserLocked()) {
return true;
}
@@ -261,7 +260,7 @@
int i = activities.size();
while (i > 0) {
i--;
- ((HistoryRecord)activities.get(i)).stopFreezingScreenLocked(true);
+ activities.get(i).stopFreezingScreenLocked(true);
}
}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index b43b86c..b29f875 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -98,6 +98,7 @@
private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
private static final String DNS_DEFAULT_SERVER2 = "4.2.2.2";
+ // resampled each time we turn on tethering - used as cache for settings/config-val
private boolean mDunRequired; // configuration info - must use DUN apn on 3g
private HierarchicalStateMachine mTetherMasterSM;
@@ -157,8 +158,7 @@
mDhcpRange[2] = DHCP_DEFAULT_RANGE2_START;
mDhcpRange[3] = DHCP_DEFAULT_RANGE2_STOP;
}
- mDunRequired = context.getResources().getBoolean(
- com.android.internal.R.bool.config_tether_dun_required);
+ mDunRequired = false; // resample when we turn on
mTetherableUsbRegexs = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_usb_regexs);
@@ -555,7 +555,11 @@
}
public boolean isDunRequired() {
- return mDunRequired;
+ boolean defaultVal = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_tether_dun_required);
+ boolean result = (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.TETHER_DUN_REQUIRED, (defaultVal ? 1 : 0)) == 1);
+ return result;
}
public String[] getTetheredIfaces() {
@@ -1263,6 +1267,7 @@
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
+ mDunRequired = isDunRequired();
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
Log.d(TAG, "Tether Mode requested by " + who.toString());
mNotifyList.add(who);
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/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/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/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
index 4cbfc87..05527af 100644
--- a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
+++ b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
@@ -55,6 +55,35 @@
this.types = types;
}
+ // data[0] = name
+ // data[1] = apn
+ // data[2] = proxy
+ // data[3] = port
+ // data[4] = username
+ // data[5] = password
+ // data[6] = server
+ // data[7] = mmsc
+ // data[8] = mmsproxy
+ // data[9] = mmsport
+ // data[10] = mcc
+ // data[11] = mnc
+ // data[12] = auth
+ // data[13] = first type...
+ public static ApnSetting fromString(String data) {
+ if (data == null) return null;
+ String[] a = data.split("\\s*,\\s*");
+ if (a.length < 14) return null;
+ int authType = 0;
+ try {
+ authType = Integer.parseInt(a[12]);
+ } catch (Exception e) {
+ }
+ String[] typeArray = new String[a.length - 13];
+ System.arraycopy(a, 13, typeArray, 0, a.length - 13);
+ return new ApnSetting(-1,a[10]+a[11],a[0],a[1],a[2],a[3],a[7],a[8],
+ a[9],a[4],a[5],authType,typeArray);
+ }
+
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(carrier)
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 9bc0b26..f6d4491 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -48,6 +48,7 @@
import android.util.EventLog;
import android.util.Log;
+import com.android.internal.R;
import com.android.internal.telephony.DataCallState;
import com.android.internal.telephony.DataConnection;
import com.android.internal.telephony.DataConnectionTracker;
@@ -365,6 +366,10 @@
@Override
protected boolean isApnTypeAvailable(String type) {
+ if (type.equals(Phone.APN_TYPE_DUN)) {
+ return (fetchDunApn() != null);
+ }
+
if (allApns != null) {
for (ApnSetting apn : allApns) {
if (apn.canHandleType(type)) {
@@ -1302,6 +1307,17 @@
}
}
+ private ApnSetting fetchDunApn() {
+ Context c = phone.getContext();
+ String apnData = Settings.Secure.getString(c.getContentResolver(),
+ Settings.Secure.TETHER_DUN_APN);
+ ApnSetting dunSetting = ApnSetting.fromString(apnData);
+ if (dunSetting != null) return dunSetting;
+
+ apnData = c.getResources().getString(R.string.config_tether_apndata);
+ return ApnSetting.fromString(apnData);
+ }
+
/**
*
* @return waitingApns list to be used to create PDP
@@ -1309,6 +1325,13 @@
*/
private ArrayList<ApnSetting> buildWaitingApns() {
ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
+
+ if (mRequestedApnType.equals(Phone.APN_TYPE_DUN)) {
+ ApnSetting dun = fetchDunApn();
+ if (dun != null) apnList.add(dun);
+ return apnList;
+ }
+
String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
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/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 4d2d046..d6cfb12 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -893,6 +893,11 @@
return true;
}
+ if (strcmp(name, "xhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_MEDIUM*2;
+ return true;
+ }
+
char* c = (char*)name;
while (*c >= '0' && *c <= '9') {
c++;
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);