summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/accounts/AccountManagerService.java8
-rw-r--r--core/java/android/app/ActivityManager.java16
-rw-r--r--core/java/android/app/ActivityManagerNative.java41
-rw-r--r--core/java/android/app/ActivityThread.java16
-rw-r--r--core/java/android/app/ContextImpl.java43
-rw-r--r--core/java/android/app/Dialog.java2
-rw-r--r--core/java/android/app/IActivityManager.java8
-rw-r--r--core/java/android/app/ListActivity.java6
-rw-r--r--core/java/android/app/NativeActivity.java141
-rw-r--r--core/java/android/app/Notification.java38
-rw-r--r--core/java/android/app/StatusBarManager.java34
-rw-r--r--core/java/android/bluetooth/BluetoothDeviceProfileState.java665
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java95
-rw-r--r--core/java/android/bluetooth/BluetoothProfileState.java144
-rw-r--r--core/java/android/bluetooth/IBluetooth.aidl4
-rw-r--r--core/java/android/bluetooth/IBluetoothA2dp.aidl3
-rw-r--r--core/java/android/bluetooth/IBluetoothHeadset.aidl10
-rw-r--r--core/java/android/content/ContentService.java3
-rw-r--r--core/java/android/content/IntentSender.java20
-rw-r--r--core/java/android/content/SyncStorageEngine.java4
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java43
-rw-r--r--core/java/android/content/pm/ComponentInfo.java8
-rw-r--r--core/java/android/content/pm/PackageItemInfo.java47
-rw-r--r--core/java/android/content/pm/PackageManager.java73
-rw-r--r--core/java/android/content/pm/PackageParser.java98
-rw-r--r--core/java/android/content/res/CompatibilityInfo.java39
-rw-r--r--core/java/android/content/res/Configuration.java3
-rw-r--r--core/java/android/database/Cursor.java3
-rw-r--r--core/java/android/database/sqlite/SQLiteCursor.java3
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java32
-rw-r--r--core/java/android/database/sqlite/SQLiteOpenHelper.java10
-rw-r--r--core/java/android/database/sqlite/SQLiteProgram.java3
-rw-r--r--core/java/android/database/sqlite/SQLiteQuery.java3
-rw-r--r--core/java/android/database/sqlite/SQLiteStatement.java9
-rw-r--r--core/java/android/hardware/Camera.java277
-rw-r--r--core/java/android/inputmethodservice/IInputMethodSessionWrapper.java15
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java52
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java16
-rw-r--r--core/java/android/net/MobileDataStateTracker.java3
-rw-r--r--core/java/android/os/BatteryStats.java83
-rw-r--r--core/java/android/os/Build.java4
-rw-r--r--core/java/android/os/Environment.java48
-rw-r--r--core/java/android/os/MessageQueue.java60
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java4
-rw-r--r--core/java/android/os/RecoverySystem.java17
-rw-r--r--core/java/android/os/StrictMode.java219
-rw-r--r--core/java/android/pim/vcard/VCardBuilder.java49
-rw-r--r--core/java/android/pim/vcard/VCardConfig.java31
-rw-r--r--core/java/android/pim/vcard/VCardEntry.java5
-rw-r--r--core/java/android/preference/DialogPreference.java3
-rw-r--r--core/java/android/provider/MediaStore.java8
-rw-r--r--core/java/android/provider/Settings.java15
-rw-r--r--core/java/android/server/BluetoothA2dpService.java109
-rw-r--r--core/java/android/server/BluetoothEventLoop.java1
-rw-r--r--core/java/android/server/BluetoothService.java146
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java25
-rw-r--r--core/java/android/text/util/Rfc822Tokenizer.java12
-rw-r--r--core/java/android/util/DisplayMetrics.java13
-rw-r--r--core/java/android/util/Log.java35
-rwxr-xr-x[-rw-r--r--]core/java/android/view/KeyEvent.java8
-rw-r--r--core/java/android/view/MotionEvent.java19
-rw-r--r--core/java/android/view/Surface.java10
-rw-r--r--core/java/android/view/SurfaceHolder.java1
-rw-r--r--core/java/android/view/SurfaceView.java13
-rw-r--r--core/java/android/view/VelocityTracker.java2
-rw-r--r--core/java/android/view/ViewRoot.java308
-rw-r--r--core/java/android/view/Window.java8
-rw-r--r--core/java/android/webkit/MimeTypeMap.java2
-rw-r--r--core/java/android/webkit/WebView.java42
-rw-r--r--core/java/android/widget/DatePicker.java1
-rw-r--r--core/java/android/widget/Gallery.java2
-rw-r--r--core/java/android/widget/MediaController.java2
-rw-r--r--core/java/android/widget/ProgressBar.java16
-rw-r--r--core/java/android/widget/RemoteViews.java9
-rw-r--r--core/java/android/widget/TextView.java4
-rw-r--r--core/java/com/android/internal/app/ExternalMediaFormatActivity.java5
-rw-r--r--core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java156
-rwxr-xr-xcore/java/com/android/internal/app/NetInitiatedActivity.java9
-rw-r--r--core/java/com/android/internal/app/RingtonePickerActivity.java2
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java236
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java4
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl34
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl42
-rw-r--r--core/java/com/android/internal/statusbar/StatusBarIcon.aidl (renamed from core/java/android/app/IStatusBar.aidl)21
-rw-r--r--core/java/com/android/internal/statusbar/StatusBarIcon.java105
-rw-r--r--core/java/com/android/internal/statusbar/StatusBarIconList.aidl20
-rw-r--r--core/java/com/android/internal/statusbar/StatusBarIconList.java161
-rw-r--r--core/java/com/android/internal/statusbar/StatusBarNotification.aidl20
-rw-r--r--core/java/com/android/internal/statusbar/StatusBarNotification.java116
-rw-r--r--core/java/com/android/internal/statusbar/StatusBarNotificationList.aidl20
-rw-r--r--core/java/com/android/internal/util/HierarchicalStateMachine.java244
-rw-r--r--core/java/com/android/internal/view/BaseSurfaceHolder.java31
-rw-r--r--core/java/com/android/internal/view/IInputMethodSession.aidl2
-rw-r--r--core/java/com/android/internal/view/RootViewSurfaceTaker.java11
-rw-r--r--core/java/com/android/internal/widget/DigitalClock.java4
-rw-r--r--core/java/com/google/android/mms/ContentType.java1
-rw-r--r--core/java/com/google/android/mms/pdu/PduParser.java53
97 files changed, 4148 insertions, 521 deletions
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 1cd7aa7..1d9e0f1 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -1483,7 +1483,13 @@ public class AccountManagerService
}
private static String getDatabaseName() {
- return DATABASE_NAME;
+ if(Environment.isEncryptedFilesystemEnabled()) {
+ // Hard-coded path in case of encrypted file system
+ return Environment.getSystemSecureDirectory().getPath() + File.separator + DATABASE_NAME;
+ } else {
+ // Regular path in case of non-encrypted file system
+ return DATABASE_NAME;
+ }
}
private class DatabaseHelper extends SQLiteOpenHelper {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c9096cf..7f95bf5 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -727,11 +727,25 @@ public class ActivityManager {
/**
* Constant for {@link #importance}: this process is running something
- * that is considered to be actively visible to the user.
+ * that is actively visible to the user, though not in the immediate
+ * foreground.
*/
public static final int IMPORTANCE_VISIBLE = 200;
/**
+ * Constant for {@link #importance}: this process is running something
+ * that is considered to be actively perceptible to the user. An
+ * example would be an application performing background music playback.
+ */
+ public static final int IMPORTANCE_PERCEPTIBLE = 130;
+
+ /**
+ * 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 = 170;
+
+ /**
* Constant for {@link #importance}: this process is contains services
* that should remain running.
*/
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f694285..b4c7edc 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1062,6 +1062,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder app = data.readStrongBinder();
+ ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data);
+ handleApplicationStrictModeViolation(app, ci);
+ reply.writeNoException();
+ return true;
+ }
+
case SIGNAL_PERSISTENT_PROCESSES_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int sig = data.readInt();
@@ -1251,6 +1260,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
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);
@@ -2516,6 +2532,7 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
data.recycle();
}
+
public boolean handleApplicationWtf(IBinder app, String tag,
ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
{
@@ -2533,6 +2550,20 @@ class ActivityManagerProxy implements IActivityManager
return res;
}
+ public void handleApplicationStrictModeViolation(IBinder app,
+ ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(app);
+ crashInfo.writeToParcel(data, 0);
+ mRemote.transact(HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ reply.recycle();
+ data.recycle();
+ }
+
public void signalPersistentProcesses(int sig) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2758,5 +2789,15 @@ class ActivityManagerProxy implements IActivityManager
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/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 773c344..49f1a8f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -42,6 +42,7 @@ import android.database.sqlite.SQLiteDebug;
import android.database.sqlite.SQLiteDebug.DbStats;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
@@ -53,6 +54,7 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.util.AndroidRuntimeException;
import android.util.Config;
@@ -4132,6 +4134,20 @@ public final class ActivityThread {
data.info = getPackageInfoNoCheck(data.appInfo);
/**
+ * For system applications on userdebug/eng builds, log stack
+ * traces of disk and network access to dropbox for analysis.
+ */
+ if ((data.appInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0 &&
+ !"user".equals(Build.TYPE)) {
+ StrictMode.setDropBoxManager(ContextImpl.createDropBoxManager());
+ StrictMode.setThreadBlockingPolicy(
+ StrictMode.DISALLOW_DISK_WRITE |
+ StrictMode.DISALLOW_DISK_READ |
+ StrictMode.DISALLOW_NETWORK |
+ StrictMode.PENALTY_DROPBOX);
+ }
+
+ /**
* Switch this process to density compatibility mode if needed.
*/
if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f471f57..bcdfe59 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1157,12 +1157,16 @@ class ContextImpl extends Context {
return mAudioManager;
}
+ /* package */ static DropBoxManager createDropBoxManager() {
+ IBinder b = ServiceManager.getService(DROPBOX_SERVICE);
+ IDropBoxManagerService service = IDropBoxManagerService.Stub.asInterface(b);
+ return new DropBoxManager(service);
+ }
+
private DropBoxManager getDropBoxManager() {
synchronized (mSync) {
if (mDropBoxManager == null) {
- IBinder b = ServiceManager.getService(DROPBOX_SERVICE);
- IDropBoxManagerService service = IDropBoxManagerService.Stub.asInterface(b);
- mDropBoxManager = new DropBoxManager(service);
+ mDropBoxManager = createDropBoxManager();
}
}
return mDropBoxManager;
@@ -2173,6 +2177,39 @@ class ContextImpl extends Context {
throws NameNotFoundException {
return getApplicationIcon(getApplicationInfo(packageName, 0));
}
+
+ @Override
+ public Drawable getActivityLogo(ComponentName activityName)
+ throws NameNotFoundException {
+ return getActivityInfo(activityName, 0).loadLogo(this);
+ }
+
+ @Override
+ public Drawable getActivityLogo(Intent intent)
+ throws NameNotFoundException {
+ if (intent.getComponent() != null) {
+ return getActivityLogo(intent.getComponent());
+ }
+
+ ResolveInfo info = resolveActivity(
+ intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if (info != null) {
+ return info.activityInfo.loadLogo(this);
+ }
+
+ throw new NameNotFoundException(intent.toUri(0));
+ }
+
+ @Override
+ public Drawable getApplicationLogo(ApplicationInfo info) {
+ return info.loadLogo(this);
+ }
+
+ @Override
+ public Drawable getApplicationLogo(String packageName)
+ throws NameNotFoundException {
+ return getApplicationLogo(getApplicationInfo(packageName, 0));
+ }
@Override public Resources getResourcesForActivity(
ComponentName activityName) throws NameNotFoundException {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 0235599..da8c9e5 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -138,7 +138,7 @@ public class Dialog implements DialogInterface, Window.Callback,
public Dialog(Context context, int theme) {
mContext = new ContextThemeWrapper(
context, theme == 0 ? com.android.internal.R.style.Theme_Dialog : theme);
- mWindowManager = (WindowManager)context.getSystemService("window");
+ mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
w.setCallback(this);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 31f0a63..ca09290 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -256,7 +256,9 @@ public interface IActivityManager extends IInterface {
ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
public boolean handleApplicationWtf(IBinder app, String tag,
ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
-
+ public void handleApplicationStrictModeViolation(IBinder app,
+ ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
+
/*
* This will deliver the specified signal to all the persistent processes. Currently only
* SIGUSR1 is delivered. All others are ignored.
@@ -303,6 +305,8 @@ public interface IActivityManager extends IInterface {
public boolean isUserAMonkey() throws RemoteException;
+ public void finishHeavyWeightApp() throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -513,4 +517,6 @@ public interface IActivityManager extends IInterface {
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;
+ int HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+109;
}
diff --git a/core/java/android/app/ListActivity.java b/core/java/android/app/ListActivity.java
index 84a57b5..4bf5518 100644
--- a/core/java/android/app/ListActivity.java
+++ b/core/java/android/app/ListActivity.java
@@ -18,9 +18,7 @@ package android.app;
import android.os.Bundle;
import android.os.Handler;
-import android.view.KeyEvent;
import android.view.View;
-import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
@@ -68,7 +66,7 @@ import android.widget.ListView;
* android:layout_weight="1"
* android:drawSelectorOnTop="false"/>
*
- * <TextView id="@id/android:empty"
+ * <TextView android:id="@id/android:empty"
* android:layout_width="match_parent"
* android:layout_height="match_parent"
* android:background="#FF0000"
@@ -316,7 +314,7 @@ public class ListActivity extends Activity {
}
private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {
- public void onItemClick(AdapterView parent, View v, int position, long id)
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id)
{
onListItemClick((ListView)parent, v, position, id);
}
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
new file mode 100644
index 0000000..fd20b71
--- /dev/null
+++ b/core/java/android/app/NativeActivity.java
@@ -0,0 +1,141 @@
+package android.app;
+
+import dalvik.system.PathClassLoader;
+
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+
+import java.io.File;
+
+/**
+ * Convenience for implementing an activity that will be implemented
+ * purely in native code. That is, a game (or game-like thing).
+ */
+public class NativeActivity extends Activity implements SurfaceHolder.Callback {
+ public static final String META_DATA_LIB_NAME = "android.app.lib_name";
+
+ private int mNativeHandle;
+
+ private native int loadNativeCode(String path);
+ private native void unloadNativeCode(int handle);
+
+ private native void onStartNative(int handle);
+ private native void onResumeNative(int handle);
+ private native void onSaveInstanceStateNative(int handle);
+ private native void onPauseNative(int handle);
+ private native void onStopNative(int handle);
+ private native void onLowMemoryNative(int handle);
+ private native void onWindowFocusChangedNative(int handle, boolean focused);
+ private native void onSurfaceCreatedNative(int handle, SurfaceHolder holder);
+ private native void onSurfaceChangedNative(int handle, SurfaceHolder holder,
+ int format, int width, int height);
+ private native void onSurfaceDestroyedNative(int handle, SurfaceHolder holder);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ String libname = "main";
+ ActivityInfo ai;
+
+ getWindow().takeSurface(this);
+
+ try {
+ ai = getPackageManager().getActivityInfo(
+ getIntent().getComponent(), PackageManager.GET_META_DATA);
+ if (ai.metaData != null) {
+ String ln = ai.metaData.getString(META_DATA_LIB_NAME);
+ if (ln != null) libname = ln;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("Error getting activity info", e);
+ }
+
+ String path = null;
+
+ if ((ai.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) == 0) {
+ // If the application does not have (Java) code, then no ClassLoader
+ // has been set up for it. We will need to do our own search for
+ // the native code.
+ path = ai.applicationInfo.dataDir + "/lib/" + System.mapLibraryName(libname);
+ if (!(new File(path)).exists()) {
+ path = null;
+ }
+ }
+
+ if (path == null) {
+ path = ((PathClassLoader)getClassLoader()).findLibrary(libname);
+ }
+
+ if (path == null) {
+ throw new IllegalArgumentException("Unable to find native library: " + libname);
+ }
+
+ mNativeHandle = loadNativeCode(path);
+ if (mNativeHandle == 0) {
+ throw new IllegalArgumentException("Unable to load native library: " + path);
+ }
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ unloadNativeCode(mNativeHandle);
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ onPauseNative(mNativeHandle);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ onResumeNative(mNativeHandle);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ onSaveInstanceStateNative(mNativeHandle);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ onStartNative(mNativeHandle);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ onStopNative(mNativeHandle);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ onLowMemoryNative(mNativeHandle);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ onWindowFocusChangedNative(mNativeHandle, hasFocus);
+ }
+
+ public void surfaceCreated(SurfaceHolder holder) {
+ onSurfaceCreatedNative(mNativeHandle, holder);
+ }
+
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ onSurfaceChangedNative(mNativeHandle, holder, format, width, height);
+ }
+
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ onSurfaceDestroyedNative(mNativeHandle, holder);
+ }
+}
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 @@ public class Notification implements Parcelable
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.RemoteException;
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 class StatusBarManager {
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 class StatusBarManager {
*/
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 class StatusBarManager {
*/
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/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
new file mode 100644
index 0000000..8e655e2
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -0,0 +1,665 @@
+/*
+ * 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 android.bluetooth;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Message;
+import android.server.BluetoothA2dpService;
+import android.server.BluetoothService;
+import android.util.Log;
+
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+/**
+ * This class is the Profile connection state machine associated with a remote
+ * device. When the device bonds an instance of this class is created.
+ * This tracks incoming and outgoing connections of all the profiles. Incoming
+ * connections are preferred over outgoing connections and HFP preferred over
+ * A2DP. When the device is unbonded, the instance is removed.
+ *
+ * States:
+ * {@link BondedDevice}: This state represents a bonded device. When in this
+ * state none of the profiles are in transition states.
+ *
+ * {@link OutgoingHandsfree}: Handsfree profile connection is in a transition
+ * state because of a outgoing Connect or Disconnect.
+ *
+ * {@link IncomingHandsfree}: Handsfree profile connection is in a transition
+ * state because of a incoming Connect or Disconnect.
+ *
+ * {@link IncomingA2dp}: A2dp profile connection is in a transition
+ * state because of a incoming Connect or Disconnect.
+ *
+ * {@link OutgoingA2dp}: A2dp profile connection is in a transition
+ * state because of a outgoing Connect or Disconnect.
+ *
+ * Todo(): Write tests for this class, when the Android Mock support is completed.
+ * @hide
+ */
+public final class BluetoothDeviceProfileState extends HierarchicalStateMachine {
+ private static final String TAG = "BluetoothDeviceProfileState";
+ private static final boolean DBG = true; //STOPSHIP - Change to false
+
+ public static final int CONNECT_HFP_OUTGOING = 1;
+ public static final int CONNECT_HFP_INCOMING = 2;
+ public static final int CONNECT_A2DP_OUTGOING = 3;
+ public static final int CONNECT_A2DP_INCOMING = 4;
+
+ public static final int DISCONNECT_HFP_OUTGOING = 5;
+ private static final int DISCONNECT_HFP_INCOMING = 6;
+ public static final int DISCONNECT_A2DP_OUTGOING = 7;
+ public static final int DISCONNECT_A2DP_INCOMING = 8;
+
+ public static final int UNPAIR = 9;
+ public static final int AUTO_CONNECT_PROFILES = 10;
+ public static final int TRANSITION_TO_STABLE = 11;
+
+ private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs
+
+ private BondedDevice mBondedDevice = new BondedDevice();
+ private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree();
+ private IncomingHandsfree mIncomingHandsfree = new IncomingHandsfree();
+ private IncomingA2dp mIncomingA2dp = new IncomingA2dp();
+ private OutgoingA2dp mOutgoingA2dp = new OutgoingA2dp();
+
+ private Context mContext;
+ private BluetoothService mService;
+ private BluetoothA2dpService mA2dpService;
+ private BluetoothHeadset mHeadsetService;
+ private boolean mHeadsetServiceConnected;
+
+ private BluetoothDevice mDevice;
+ private int mHeadsetState;
+ private int mA2dpState;
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (!device.equals(mDevice)) return;
+
+ if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
+ int oldState = intent.getIntExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, 0);
+ int initiator = intent.getIntExtra(
+ BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR,
+ BluetoothHeadset.LOCAL_DISCONNECT);
+ mHeadsetState = newState;
+ if (newState == BluetoothHeadset.STATE_DISCONNECTED &&
+ initiator == BluetoothHeadset.REMOTE_DISCONNECT) {
+ sendMessage(DISCONNECT_HFP_INCOMING);
+ }
+ if (newState == BluetoothHeadset.STATE_CONNECTED ||
+ newState == BluetoothHeadset.STATE_DISCONNECTED) {
+ sendMessage(TRANSITION_TO_STABLE);
+ }
+ } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
+ int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0);
+ mA2dpState = newState;
+ if ((oldState == BluetoothA2dp.STATE_CONNECTED ||
+ oldState == BluetoothA2dp.STATE_PLAYING) &&
+ newState == BluetoothA2dp.STATE_DISCONNECTED) {
+ sendMessage(DISCONNECT_A2DP_INCOMING);
+ }
+ if (newState == BluetoothA2dp.STATE_CONNECTED ||
+ newState == BluetoothA2dp.STATE_DISCONNECTED) {
+ sendMessage(TRANSITION_TO_STABLE);
+ }
+ } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
+ if (!getCurrentState().equals(mBondedDevice)) {
+ Log.e(TAG, "State is: " + getCurrentState());
+ return;
+ }
+ Message msg = new Message();
+ msg.what = AUTO_CONNECT_PROFILES;
+ sendMessageDelayed(msg, AUTO_CONNECT_DELAY);
+ }
+ }
+ };
+
+ private boolean isPhoneDocked(BluetoothDevice autoConnectDevice) {
+ // This works only because these broadcast intents are "sticky"
+ Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
+ if (i != null) {
+ int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ BluetoothDevice device = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (device != null && autoConnectDevice.equals(device)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public BluetoothDeviceProfileState(Context context, String address,
+ BluetoothService service, BluetoothA2dpService a2dpService) {
+ super(address);
+ mContext = context;
+ mDevice = new BluetoothDevice(address);
+ mService = service;
+ mA2dpService = a2dpService;
+
+ addState(mBondedDevice);
+ addState(mOutgoingHandsfree);
+ addState(mIncomingHandsfree);
+ addState(mIncomingA2dp);
+ addState(mOutgoingA2dp);
+ setInitialState(mBondedDevice);
+
+ IntentFilter filter = new IntentFilter();
+ // Fine-grained state broadcasts
+ filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+ filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+ filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+
+ HeadsetServiceListener l = new HeadsetServiceListener();
+ }
+
+ private class HeadsetServiceListener implements BluetoothHeadset.ServiceListener {
+ public HeadsetServiceListener() {
+ mHeadsetService = new BluetoothHeadset(mContext, this);
+ }
+ public void onServiceConnected() {
+ synchronized(BluetoothDeviceProfileState.this) {
+ mHeadsetServiceConnected = true;
+ }
+ }
+ public void onServiceDisconnected() {
+ synchronized(BluetoothDeviceProfileState.this) {
+ mHeadsetServiceConnected = false;
+ }
+ }
+ }
+
+ private class BondedDevice extends HierarchicalState {
+ @Override
+ protected void enter() {
+ log("Entering ACL Connected state with: " + getCurrentMessage().what);
+ Message m = new Message();
+ m.copyFrom(getCurrentMessage());
+ sendMessageAtFrontOfQueue(m);
+ }
+ @Override
+ protected boolean processMessage(Message message) {
+ log("ACL Connected State -> Processing Message: " + message.what);
+ switch(message.what) {
+ case CONNECT_HFP_OUTGOING:
+ case DISCONNECT_HFP_OUTGOING:
+ transitionTo(mOutgoingHandsfree);
+ break;
+ case CONNECT_HFP_INCOMING:
+ transitionTo(mIncomingHandsfree);
+ break;
+ case DISCONNECT_HFP_INCOMING:
+ transitionTo(mIncomingHandsfree);
+ break;
+ case CONNECT_A2DP_OUTGOING:
+ case DISCONNECT_A2DP_OUTGOING:
+ transitionTo(mOutgoingA2dp);
+ break;
+ case CONNECT_A2DP_INCOMING:
+ case DISCONNECT_A2DP_INCOMING:
+ transitionTo(mIncomingA2dp);
+ break;
+ case UNPAIR:
+ if (mHeadsetState != BluetoothHeadset.STATE_DISCONNECTED) {
+ sendMessage(DISCONNECT_HFP_OUTGOING);
+ deferMessage(message);
+ break;
+ } else if (mA2dpState != BluetoothA2dp.STATE_DISCONNECTED) {
+ sendMessage(DISCONNECT_A2DP_OUTGOING);
+ deferMessage(message);
+ break;
+ }
+ processCommand(UNPAIR);
+ break;
+ case AUTO_CONNECT_PROFILES:
+ if (isPhoneDocked(mDevice)) {
+ // Don't auto connect to docks.
+ break;
+ } else if (!mHeadsetServiceConnected) {
+ deferMessage(message);
+ } else {
+ if (mHeadsetService.getPriority(mDevice) ==
+ BluetoothHeadset.PRIORITY_AUTO_CONNECT &&
+ !mHeadsetService.isConnected(mDevice)) {
+ mHeadsetService.connectHeadset(mDevice);
+ }
+ if (mA2dpService != null &&
+ mA2dpService.getSinkPriority(mDevice) ==
+ BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
+ mA2dpService.getConnectedSinks().length == 0) {
+ mA2dpService.connectSink(mDevice);
+ }
+ }
+ break;
+ case TRANSITION_TO_STABLE:
+ // ignore.
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ private class OutgoingHandsfree extends HierarchicalState {
+ private boolean mStatus = false;
+ private int mCommand;
+
+ @Override
+ protected void enter() {
+ log("Entering OutgoingHandsfree state with: " + getCurrentMessage().what);
+ mCommand = getCurrentMessage().what;
+ if (mCommand != CONNECT_HFP_OUTGOING &&
+ mCommand != DISCONNECT_HFP_OUTGOING) {
+ Log.e(TAG, "Error: OutgoingHandsfree state with command:" + mCommand);
+ }
+ mStatus = processCommand(mCommand);
+ if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+ }
+
+ @Override
+ protected boolean processMessage(Message message) {
+ log("OutgoingHandsfree State -> Processing Message: " + message.what);
+ Message deferMsg = new Message();
+ int command = message.what;
+ switch(command) {
+ case CONNECT_HFP_OUTGOING:
+ if (command != mCommand) {
+ // Disconnect followed by a connect - defer
+ deferMessage(message);
+ }
+ break;
+ case CONNECT_HFP_INCOMING:
+ if (mCommand == CONNECT_HFP_OUTGOING) {
+ // Cancel outgoing connect, accept incoming
+ cancelCommand(CONNECT_HFP_OUTGOING);
+ transitionTo(mIncomingHandsfree);
+ } else {
+ // We have done the disconnect but we are not
+ // sure which state we are in at this point.
+ deferMessage(message);
+ }
+ break;
+ case CONNECT_A2DP_INCOMING:
+ // accept incoming A2DP, retry HFP_OUTGOING
+ transitionTo(mIncomingA2dp);
+
+ if (mStatus) {
+ deferMsg.what = mCommand;
+ deferMessage(deferMsg);
+ }
+ break;
+ case CONNECT_A2DP_OUTGOING:
+ deferMessage(message);
+ break;
+ case DISCONNECT_HFP_OUTGOING:
+ if (mCommand == CONNECT_HFP_OUTGOING) {
+ // Cancel outgoing connect
+ cancelCommand(CONNECT_HFP_OUTGOING);
+ processCommand(DISCONNECT_HFP_OUTGOING);
+ }
+ // else ignore
+ break;
+ case DISCONNECT_HFP_INCOMING:
+ // When this happens the socket would be closed and the headset
+ // state moved to DISCONNECTED, cancel the outgoing thread.
+ // if it still is in CONNECTING state
+ cancelCommand(CONNECT_HFP_OUTGOING);
+ break;
+ case DISCONNECT_A2DP_OUTGOING:
+ deferMessage(message);
+ break;
+ case DISCONNECT_A2DP_INCOMING:
+ // Bluez will handle the disconnect. If because of this the outgoing
+ // handsfree connection has failed, then retry.
+ if (mStatus) {
+ deferMsg.what = mCommand;
+ deferMessage(deferMsg);
+ }
+ break;
+ case UNPAIR:
+ case AUTO_CONNECT_PROFILES:
+ deferMessage(message);
+ break;
+ case TRANSITION_TO_STABLE:
+ transitionTo(mBondedDevice);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ private class IncomingHandsfree extends HierarchicalState {
+ private boolean mStatus = false;
+ private int mCommand;
+
+ @Override
+ protected void enter() {
+ log("Entering IncomingHandsfree state with: " + getCurrentMessage().what);
+ mCommand = getCurrentMessage().what;
+ if (mCommand != CONNECT_HFP_INCOMING &&
+ mCommand != DISCONNECT_HFP_INCOMING) {
+ Log.e(TAG, "Error: IncomingHandsfree state with command:" + mCommand);
+ }
+ mStatus = processCommand(mCommand);
+ if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+ }
+
+ @Override
+ protected boolean processMessage(Message message) {
+ log("IncomingHandsfree State -> Processing Message: " + message.what);
+ switch(message.what) {
+ case CONNECT_HFP_OUTGOING:
+ deferMessage(message);
+ break;
+ case CONNECT_HFP_INCOMING:
+ // Ignore
+ Log.e(TAG, "Error: Incoming connection with a pending incoming connection");
+ break;
+ case CONNECT_A2DP_INCOMING:
+ // Serialize the commands.
+ deferMessage(message);
+ break;
+ case CONNECT_A2DP_OUTGOING:
+ deferMessage(message);
+ break;
+ case DISCONNECT_HFP_OUTGOING:
+ // We don't know at what state we are in the incoming HFP connection state.
+ // We can be changing from DISCONNECTED to CONNECTING, or
+ // from CONNECTING to CONNECTED, so serializing this command is
+ // the safest option.
+ deferMessage(message);
+ break;
+ case DISCONNECT_HFP_INCOMING:
+ // Nothing to do here, we will already be DISCONNECTED
+ // by this point.
+ break;
+ case DISCONNECT_A2DP_OUTGOING:
+ deferMessage(message);
+ break;
+ case DISCONNECT_A2DP_INCOMING:
+ // Bluez handles incoming A2DP disconnect.
+ // If this causes incoming HFP to fail, it is more of a headset problem
+ // since both connections are incoming ones.
+ break;
+ case UNPAIR:
+ case AUTO_CONNECT_PROFILES:
+ deferMessage(message);
+ break;
+ case TRANSITION_TO_STABLE:
+ transitionTo(mBondedDevice);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ private class OutgoingA2dp extends HierarchicalState {
+ private boolean mStatus = false;
+ private int mCommand;
+
+ @Override
+ protected void enter() {
+ log("Entering OutgoingA2dp state with: " + getCurrentMessage().what);
+ mCommand = getCurrentMessage().what;
+ if (mCommand != CONNECT_A2DP_OUTGOING &&
+ mCommand != DISCONNECT_A2DP_OUTGOING) {
+ Log.e(TAG, "Error: OutgoingA2DP state with command:" + mCommand);
+ }
+ mStatus = processCommand(mCommand);
+ if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+ }
+
+ @Override
+ protected boolean processMessage(Message message) {
+ log("OutgoingA2dp State->Processing Message: " + message.what);
+ Message deferMsg = new Message();
+ switch(message.what) {
+ case CONNECT_HFP_OUTGOING:
+ processCommand(CONNECT_HFP_OUTGOING);
+
+ // Don't cancel A2DP outgoing as there is no guarantee it
+ // will get canceled.
+ // It might already be connected but we might not have got the
+ // A2DP_SINK_STATE_CHANGE. Hence, no point disconnecting here.
+ // The worst case, the connection will fail, retry.
+ // The same applies to Disconnecting an A2DP connection.
+ if (mStatus) {
+ deferMsg.what = mCommand;
+ deferMessage(deferMsg);
+ }
+ break;
+ case CONNECT_HFP_INCOMING:
+ processCommand(CONNECT_HFP_INCOMING);
+
+ // Don't cancel A2DP outgoing as there is no guarantee
+ // it will get canceled.
+ // The worst case, the connection will fail, retry.
+ if (mStatus) {
+ deferMsg.what = mCommand;
+ deferMessage(deferMsg);
+ }
+ break;
+ case CONNECT_A2DP_INCOMING:
+ // Bluez will take care of conflicts between incoming and outgoing
+ // connections.
+ transitionTo(mIncomingA2dp);
+ break;
+ case CONNECT_A2DP_OUTGOING:
+ // Ignore
+ break;
+ case DISCONNECT_HFP_OUTGOING:
+ deferMessage(message);
+ break;
+ case DISCONNECT_HFP_INCOMING:
+ // At this point, we are already disconnected
+ // with HFP. Sometimes A2DP connection can
+ // fail due to the disconnection of HFP. So add a retry
+ // for the A2DP.
+ if (mStatus) {
+ deferMsg.what = mCommand;
+ deferMessage(deferMsg);
+ }
+ break;
+ case DISCONNECT_A2DP_OUTGOING:
+ processCommand(DISCONNECT_A2DP_OUTGOING);
+ break;
+ case DISCONNECT_A2DP_INCOMING:
+ // Ignore, will be handled by Bluez
+ break;
+ case UNPAIR:
+ case AUTO_CONNECT_PROFILES:
+ deferMessage(message);
+ break;
+ case TRANSITION_TO_STABLE:
+ transitionTo(mBondedDevice);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ private class IncomingA2dp extends HierarchicalState {
+ private boolean mStatus = false;
+ private int mCommand;
+
+ @Override
+ protected void enter() {
+ log("Entering IncomingA2dp state with: " + getCurrentMessage().what);
+ mCommand = getCurrentMessage().what;
+ if (mCommand != CONNECT_A2DP_INCOMING &&
+ mCommand != DISCONNECT_A2DP_INCOMING) {
+ Log.e(TAG, "Error: IncomingA2DP state with command:" + mCommand);
+ }
+ mStatus = processCommand(mCommand);
+ if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+ }
+
+ @Override
+ protected boolean processMessage(Message message) {
+ log("IncomingA2dp State->Processing Message: " + message.what);
+ Message deferMsg = new Message();
+ switch(message.what) {
+ case CONNECT_HFP_OUTGOING:
+ deferMessage(message);
+ break;
+ case CONNECT_HFP_INCOMING:
+ // Shouldn't happen, but serialize the commands.
+ deferMessage(message);
+ break;
+ case CONNECT_A2DP_INCOMING:
+ // ignore
+ break;
+ case CONNECT_A2DP_OUTGOING:
+ // Defer message and retry
+ deferMessage(message);
+ break;
+ case DISCONNECT_HFP_OUTGOING:
+ deferMessage(message);
+ break;
+ case DISCONNECT_HFP_INCOMING:
+ // Shouldn't happen but if does, we can handle it.
+ // Depends if the headset can handle it.
+ // Incoming A2DP will be handled by Bluez, Disconnect HFP
+ // the socket would have already been closed.
+ // ignore
+ break;
+ case DISCONNECT_A2DP_OUTGOING:
+ deferMessage(message);
+ break;
+ case DISCONNECT_A2DP_INCOMING:
+ // Ignore, will be handled by Bluez
+ break;
+ case UNPAIR:
+ case AUTO_CONNECT_PROFILES:
+ deferMessage(message);
+ break;
+ case TRANSITION_TO_STABLE:
+ transitionTo(mBondedDevice);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+
+
+ synchronized void cancelCommand(int command) {
+ if (command == CONNECT_HFP_OUTGOING ) {
+ // Cancel the outgoing thread.
+ if (mHeadsetServiceConnected) {
+ mHeadsetService.cancelConnectThread();
+ }
+ // HeadsetService is down. Phone process most likely crashed.
+ // The thread would have got killed.
+ }
+ }
+
+ synchronized void deferHeadsetMessage(int command) {
+ Message msg = new Message();
+ msg.what = command;
+ deferMessage(msg);
+ }
+
+ synchronized boolean processCommand(int command) {
+ log("Processing command:" + command);
+ switch(command) {
+ case CONNECT_HFP_OUTGOING:
+ if (mHeadsetService != null) {
+ return mHeadsetService.connectHeadsetInternal(mDevice);
+ }
+ break;
+ case CONNECT_HFP_INCOMING:
+ if (!mHeadsetServiceConnected) {
+ deferHeadsetMessage(command);
+ } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) {
+ return mHeadsetService.acceptIncomingConnect(mDevice);
+ } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) {
+ return mHeadsetService.createIncomingConnect(mDevice);
+ }
+ break;
+ case CONNECT_A2DP_OUTGOING:
+ if (mA2dpService != null) {
+ return mA2dpService.connectSinkInternal(mDevice);
+ }
+ break;
+ case CONNECT_A2DP_INCOMING:
+ // ignore, Bluez takes care
+ return true;
+ case DISCONNECT_HFP_OUTGOING:
+ if (!mHeadsetServiceConnected) {
+ deferHeadsetMessage(command);
+ } else {
+ if (mHeadsetService.getPriority(mDevice) ==
+ BluetoothHeadset.PRIORITY_AUTO_CONNECT) {
+ mHeadsetService.setPriority(mDevice, BluetoothHeadset.PRIORITY_ON);
+ }
+ return mHeadsetService.disconnectHeadsetInternal(mDevice);
+ }
+ break;
+ case DISCONNECT_HFP_INCOMING:
+ // ignore
+ return true;
+ case DISCONNECT_A2DP_INCOMING:
+ // ignore
+ return true;
+ case DISCONNECT_A2DP_OUTGOING:
+ if (mA2dpService != null) {
+ if (mA2dpService.getSinkPriority(mDevice) ==
+ BluetoothA2dp.PRIORITY_AUTO_CONNECT) {
+ mA2dpService.setSinkPriority(mDevice, BluetoothHeadset.PRIORITY_ON);
+ }
+ return mA2dpService.disconnectSinkInternal(mDevice);
+ }
+ break;
+ case UNPAIR:
+ return mService.removeBondInternal(mDevice.getAddress());
+ default:
+ Log.e(TAG, "Error: Unknown Command");
+ }
+ return false;
+ }
+
+ /*package*/ BluetoothDevice getDevice() {
+ return mDevice;
+ }
+
+ private void log(String message) {
+ if (DBG) {
+ Log.i(TAG, "Device:" + mDevice + " Message:" + message);
+ }
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 95e61b6..4a91a8c 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -189,11 +189,11 @@ public final class BluetoothHeadset {
* @return One of the STATE_ return codes, or STATE_ERROR if this proxy
* object is currently not connected to the Headset service.
*/
- public int getState() {
+ public int getState(BluetoothDevice device) {
if (DBG) log("getState()");
if (mService != null) {
try {
- return mService.getState();
+ return mService.getState(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
@@ -271,11 +271,11 @@ public final class BluetoothHeadset {
* be made asynchornous. Returns false if this proxy object is
* not currently connected to the Headset service.
*/
- public boolean disconnectHeadset() {
+ public boolean disconnectHeadset(BluetoothDevice device) {
if (DBG) log("disconnectHeadset()");
if (mService != null) {
try {
- mService.disconnectHeadset();
+ mService.disconnectHeadset(device);
return true;
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
@@ -395,7 +395,6 @@ public final class BluetoothHeadset {
}
return -1;
}
-
/**
* Indicates if current platform supports voice dialing over bluetooth SCO.
* @return true if voice dialing over bluetooth is supported, false otherwise.
@@ -406,6 +405,92 @@ public final class BluetoothHeadset {
com.android.internal.R.bool.config_bluetooth_sco_off_call);
}
+ /**
+ * Cancel the outgoing connection.
+ * @hide
+ */
+ public boolean cancelConnectThread() {
+ if (DBG) log("cancelConnectThread");
+ if (mService != null) {
+ try {
+ return mService.cancelConnectThread();
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Accept the incoming connection.
+ * @hide
+ */
+ public boolean acceptIncomingConnect(BluetoothDevice device) {
+ if (DBG) log("acceptIncomingConnect");
+ if (mService != null) {
+ try {
+ return mService.acceptIncomingConnect(device);
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Create the connect thread the incoming connection.
+ * @hide
+ */
+ public boolean createIncomingConnect(BluetoothDevice device) {
+ if (DBG) log("createIncomingConnect");
+ if (mService != null) {
+ try {
+ return mService.createIncomingConnect(device);
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Connect to a Bluetooth Headset.
+ * Note: This is an internal function and shouldn't be exposed
+ * @hide
+ */
+ public boolean connectHeadsetInternal(BluetoothDevice device) {
+ if (DBG) log("connectHeadsetInternal");
+ if (mService != null) {
+ try {
+ return mService.connectHeadsetInternal(device);
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Disconnect a Bluetooth Headset.
+ * Note: This is an internal function and shouldn't be exposed
+ * @hide
+ */
+ public boolean disconnectHeadsetInternal(BluetoothDevice device) {
+ if (DBG) log("disconnectHeadsetInternal");
+ if (mService != null) {
+ try {
+ return mService.disconnectHeadsetInternal(device);
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
diff --git a/core/java/android/bluetooth/BluetoothProfileState.java b/core/java/android/bluetooth/BluetoothProfileState.java
new file mode 100644
index 0000000..946dcaa
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothProfileState.java
@@ -0,0 +1,144 @@
+/*
+ * 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 android.bluetooth;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+/**
+ * This state machine is used to serialize the connections
+ * to a particular profile. Currently, we only allow one device
+ * to be connected to a particular profile.
+ * States:
+ * {@link StableState} : No pending commands. Send the
+ * command to the appropriate remote device specific state machine.
+ *
+ * {@link PendingCommandState} : A profile connection / disconnection
+ * command is being executed. This will result in a profile state
+ * change. Defer all commands.
+ * @hide
+ */
+
+public class BluetoothProfileState extends HierarchicalStateMachine {
+ private static final boolean DBG = true; // STOPSHIP - change to false.
+ private static final String TAG = "BluetoothProfileState";
+
+ public static int HFP = 0;
+ public static int A2DP = 1;
+
+ private static int TRANSITION_TO_STABLE = 100;
+
+ private int mProfile;
+ private BluetoothDevice mPendingDevice;
+ private PendingCommandState mPendingCommandState = new PendingCommandState();
+ private StableState mStableState = new StableState();
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
+ if (mProfile == HFP && (newState == BluetoothHeadset.STATE_CONNECTED ||
+ newState == BluetoothHeadset.STATE_DISCONNECTED)) {
+ sendMessage(TRANSITION_TO_STABLE);
+ }
+ } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
+ if (mProfile == A2DP && (newState == BluetoothA2dp.STATE_CONNECTED ||
+ newState == BluetoothA2dp.STATE_DISCONNECTED)) {
+ sendMessage(TRANSITION_TO_STABLE);
+ }
+ }
+ }
+ };
+
+ public BluetoothProfileState(Context context, int profile) {
+ super("BluetoothProfileState:" + profile);
+ mProfile = profile;
+ addState(mStableState);
+ addState(mPendingCommandState);
+ setInitialState(mStableState);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+ filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+ context.registerReceiver(mBroadcastReceiver, filter);
+ }
+
+ private class StableState extends HierarchicalState {
+ @Override
+ protected void enter() {
+ log("Entering Stable State");
+ mPendingDevice = null;
+ }
+
+ @Override
+ protected boolean processMessage(Message msg) {
+ if (msg.what != TRANSITION_TO_STABLE) {
+ transitionTo(mPendingCommandState);
+ }
+ return true;
+ }
+ }
+
+ private class PendingCommandState extends HierarchicalState {
+ @Override
+ protected void enter() {
+ log("Entering PendingCommandState State");
+ dispatchMessage(getCurrentMessage());
+ }
+
+ @Override
+ protected boolean processMessage(Message msg) {
+ if (msg.what == TRANSITION_TO_STABLE) {
+ transitionTo(mStableState);
+ } else {
+ dispatchMessage(msg);
+ }
+ return true;
+ }
+
+ private void dispatchMessage(Message msg) {
+ BluetoothDeviceProfileState deviceProfileMgr =
+ (BluetoothDeviceProfileState)msg.obj;
+ int cmd = msg.arg1;
+ if (mPendingDevice == null || mPendingDevice.equals(deviceProfileMgr.getDevice())) {
+ mPendingDevice = deviceProfileMgr.getDevice();
+ deviceProfileMgr.sendMessage(cmd);
+ } else {
+ Message deferMsg = new Message();
+ deferMsg.arg1 = cmd;
+ deferMsg.obj = deviceProfileMgr;
+ deferMessage(deferMsg);
+ }
+ }
+ }
+
+ private void log(String message) {
+ if (DBG) {
+ Log.i(TAG, "Message:" + message);
+ }
+ }
+}
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 0868779..ea71034 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -68,4 +68,8 @@ interface IBluetooth
int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b);
void removeServiceRecord(int handle);
+
+ boolean connectHeadset(String address);
+ boolean disconnectHeadset(String address);
+ boolean notifyIncomingConnection(String address);
}
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index 168fe3b..40f1058 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -33,4 +33,7 @@ interface IBluetoothA2dp {
int getSinkState(in BluetoothDevice device);
boolean setSinkPriority(in BluetoothDevice device, int priority);
int getSinkPriority(in BluetoothDevice device);
+
+ boolean connectSinkInternal(in BluetoothDevice device);
+ boolean disconnectSinkInternal(in BluetoothDevice device);
}
diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl
index 6cccd50..d96f0ca 100644
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -24,14 +24,20 @@ import android.bluetooth.BluetoothDevice;
* {@hide}
*/
interface IBluetoothHeadset {
- int getState();
+ int getState(in BluetoothDevice device);
BluetoothDevice getCurrentHeadset();
boolean connectHeadset(in BluetoothDevice device);
- void disconnectHeadset();
+ void disconnectHeadset(in BluetoothDevice device);
boolean isConnected(in BluetoothDevice device);
boolean startVoiceRecognition();
boolean stopVoiceRecognition();
boolean setPriority(in BluetoothDevice device, int priority);
int getPriority(in BluetoothDevice device);
int getBatteryUsageHint();
+
+ boolean createIncomingConnect(in BluetoothDevice device);
+ boolean acceptIncomingConnect(in BluetoothDevice device);
+ boolean cancelConnectThread();
+ boolean connectHeadsetInternal(in BluetoothDevice device);
+ boolean disconnectHeadsetInternal(in BluetoothDevice device);
}
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 377e383..fc2dfc0 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -537,6 +537,9 @@ public final class ContentService extends IContentService.Stub {
// Look to see if the proper child already exists
String segment = getUriSegment(uri, index);
+ if (segment == null) {
+ throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
+ }
int N = mChildren.size();
for (int i = 0; i < N; i++) {
ObserverNode node = mChildren.get(i);
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 @@ public class IntentSender implements Parcelable {
}
/**
+ * 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/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 98a4993..6413cec 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -317,7 +317,9 @@ public class SyncStorageEngine extends Handler {
if (sSyncStorageEngine != null) {
return;
}
- File dataDir = Environment.getDataDirectory();
+ // This call will return the correct directory whether Encrypted File Systems is
+ // enabled or not.
+ File dataDir = Environment.getSecureDataDirectory();
sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1577f9e..7901b155 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -174,7 +174,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* Value for {@link #flags}: true when the application's window can be
* increased in size for larger screens. Corresponds to
* {@link android.R.styleable#AndroidManifestSupportsScreens_largeScreens
- * android:smallScreens}.
+ * android:largeScreens}.
*/
public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<11;
@@ -252,6 +252,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public static final int FLAG_RESTORE_ANY_VERSION = 1<<17;
/**
+ * Value for {@link #flags}: Set to true if the application has been
+ * installed using the forward lock option.
+ *
* Value for {@link #flags}: Set to true if the application is
* currently installed on external/removable/unprotected storage. Such
* applications may not be available if their storage is not currently
@@ -262,12 +265,41 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public static final int FLAG_EXTERNAL_STORAGE = 1<<18;
/**
+ * Value for {@link #flags}: true when the application's window can be
+ * increased in size for extra large screens. Corresponds to
+ * {@link android.R.styleable#AndroidManifestSupportsScreens_xlargeScreens
+ * android:xlargeScreens}.
+ */
+ 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 &lt;application&gt; 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,
+ * and will always store its data in-the-clear.
+ *
+ * {@hide}
+ */
+ public static final int FLAG_NEVER_ENCRYPT = 1<<30;
+
+ /**
* Value for {@link #flags}: Set to true if the application has been
* installed using the forward lock option.
*
* {@hide}
*/
- public static final int FLAG_FORWARD_LOCK = 1<<20;
+ public static final int FLAG_FORWARD_LOCK = 1<<29;
/**
* Value for {@link #flags}: Set to true if the application is
@@ -275,7 +307,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
*
* {@hide}
*/
- public static final int FLAG_NATIVE_DEBUGGABLE = 1<<21;
+ public static final int FLAG_NATIVE_DEBUGGABLE = 1<<28;
/**
* Flags associated with the application. Any combination of
@@ -285,7 +317,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* {@link #FLAG_ALLOW_CLEAR_USER_DATA}, {@link #FLAG_UPDATED_SYSTEM_APP},
* {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS},
* {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
- * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_RESIZEABLE_FOR_SCREENS},
+ * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_SUPPORTS_XLARGE_SCREENS},
+ * {@link #FLAG_RESIZEABLE_FOR_SCREENS},
* {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}, {@link #FLAG_VM_SAFE_MODE}
*/
public int flags = 0;
@@ -517,7 +550,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public void disableCompatibilityMode() {
flags |= (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS |
FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS |
- FLAG_SUPPORTS_SCREEN_DENSITIES);
+ FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
}
/**
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index cafe372..f16c4ef 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -157,6 +157,14 @@ public class ComponentInfo extends PackageItemInfo {
/**
* @hide
*/
+ @Override
+ protected Drawable loadDefaultLogo(PackageManager pm) {
+ return applicationInfo.loadLogo(pm);
+ }
+
+ /**
+ * @hide
+ */
@Override protected ApplicationInfo getApplicationInfo() {
return applicationInfo;
}
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 14c0680..d73aaf6 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -67,6 +67,14 @@ public class PackageItemInfo {
public int icon;
/**
+ * A drawable resource identifier (in the package's resources) of this
+ * component's logo. Logos may be larger/wider than icons and are
+ * displayed by certain UI elements in place of a name or name/icon
+ * combination. From the "logo" attribute or, if not set, 0.
+ */
+ public int logo;
+
+ /**
* Additional meta-data associated with this component. This field
* will only be filled in if you set the
* {@link PackageManager#GET_META_DATA} flag when requesting the info.
@@ -84,6 +92,7 @@ public class PackageItemInfo {
nonLocalizedLabel = orig.nonLocalizedLabel;
if (nonLocalizedLabel != null) nonLocalizedLabel = nonLocalizedLabel.toString().trim();
icon = orig.icon;
+ logo = orig.logo;
metaData = orig.metaData;
}
@@ -152,6 +161,42 @@ public class PackageItemInfo {
}
/**
+ * Retrieve the current graphical logo associated with this item. This
+ * will call back on the given PackageManager to load the logo from
+ * the application.
+ *
+ * @param pm A PackageManager from which the logo can be loaded; usually
+ * the PackageManager from which you originally retrieved this item.
+ *
+ * @return Returns a Drawable containing the item's logo. If the item
+ * does not have a logo, this method will return null.
+ */
+ public Drawable loadLogo(PackageManager pm) {
+ if (logo != 0) {
+ Drawable d = pm.getDrawable(packageName, logo, getApplicationInfo());
+ if (d != null) {
+ return d;
+ }
+ }
+ return loadDefaultLogo(pm);
+ }
+
+ /**
+ * Retrieve the default graphical logo associated with this item.
+ *
+ * @param pm A PackageManager from which the logo can be loaded; usually
+ * the PackageManager from which you originally retrieved this item.
+ *
+ * @return Returns a Drawable containing the item's default logo
+ * or null if no default logo is available.
+ *
+ * @hide
+ */
+ protected Drawable loadDefaultLogo(PackageManager pm) {
+ return null;
+ }
+
+ /**
* Load an XML resource attached to the meta-data of this item. This will
* retrieved the name meta-data entry, and if defined call back on the
* given PackageManager to load its XML file from the application.
@@ -196,6 +241,7 @@ public class PackageItemInfo {
dest.writeInt(labelRes);
TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags);
dest.writeInt(icon);
+ dest.writeInt(logo);
dest.writeBundle(metaData);
}
@@ -206,6 +252,7 @@ public class PackageItemInfo {
nonLocalizedLabel
= TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
icon = source.readInt();
+ logo = source.readInt();
metaData = source.readBundle();
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 68b44e7..196f508 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1597,6 +1597,79 @@ public abstract class PackageManager {
throws NameNotFoundException;
/**
+ * Retrieve the logo associated with an activity. Given the full name of
+ * an activity, retrieves the information about it and calls
+ * {@link ComponentInfo#loadLogo ComponentInfo.loadLogo()} to return its logo.
+ * If the activity can not be found, NameNotFoundException is thrown.
+ *
+ * @param activityName Name of the activity whose logo is to be retrieved.
+ *
+ * @return Returns the image of the logo or null if the activity has no
+ * logo specified.
+ *
+ * @throws NameNotFoundException Thrown if the resources for the given
+ * activity could not be loaded.
+ *
+ * @see #getActivityLogo(Intent)
+ */
+ public abstract Drawable getActivityLogo(ComponentName activityName)
+ throws NameNotFoundException;
+
+ /**
+ * Retrieve the logo associated with an Intent. If intent.getClassName() is
+ * set, this simply returns the result of
+ * getActivityLogo(intent.getClassName()). Otherwise it resolves the intent's
+ * component and returns the logo associated with the resolved component.
+ * If intent.getClassName() can not be found or the Intent can not be resolved
+ * to a component, NameNotFoundException is thrown.
+ *
+ * @param intent The intent for which you would like to retrieve a logo.
+ *
+ * @return Returns the image of the logo, or null if the activity has no
+ * logo specified.
+ *
+ * @throws NameNotFoundException Thrown if the resources for application
+ * matching the given intent could not be loaded.
+ *
+ * @see #getActivityLogo(ComponentName)
+ */
+ public abstract Drawable getActivityLogo(Intent intent)
+ throws NameNotFoundException;
+
+ /**
+ * Retrieve the logo associated with an application. If it has not specified
+ * a logo, this method returns null.
+ *
+ * @param info Information about application being queried.
+ *
+ * @return Returns the image of the logo, or null if no logo is specified
+ * by the application.
+ *
+ * @see #getApplicationLogo(String)
+ */
+ public abstract Drawable getApplicationLogo(ApplicationInfo info);
+
+ /**
+ * Retrieve the logo associated with an application. Given the name of the
+ * application's package, retrieves the information about it and calls
+ * getApplicationLogo() to return its logo. If the application can not be
+ * found, NameNotFoundException is thrown.
+ *
+ * @param packageName Name of the package whose application logo is to be
+ * retrieved.
+ *
+ * @return Returns the image of the logo, or null if no application logo
+ * has been specified.
+ *
+ * @throws NameNotFoundException Thrown if the resources for the given
+ * application could not be loaded.
+ *
+ * @see #getApplicationLogo(ApplicationInfo)
+ */
+ public abstract Drawable getApplicationLogo(String packageName)
+ throws NameNotFoundException;
+
+ /**
* Retrieve text from a package. This is a low-level API used by
* the various package manager info structures (such as
* {@link ComponentInfo} to implement retrieval of their associated
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2a20a2d..a5f5acc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -105,17 +105,19 @@ public class PackageParser {
final int nameRes;
final int labelRes;
final int iconRes;
+ final int logoRes;
String tag;
TypedArray sa;
ParsePackageItemArgs(Package _owner, String[] _outError,
- int _nameRes, int _labelRes, int _iconRes) {
+ int _nameRes, int _labelRes, int _iconRes, int _logoRes) {
owner = _owner;
outError = _outError;
nameRes = _nameRes;
labelRes = _labelRes;
iconRes = _iconRes;
+ logoRes = _logoRes;
}
}
@@ -127,10 +129,10 @@ public class PackageParser {
int flags;
ParseComponentArgs(Package _owner, String[] _outError,
- int _nameRes, int _labelRes, int _iconRes,
+ int _nameRes, int _labelRes, int _iconRes, int _logoRes,
String[] _sepProcesses, int _processRes,
int _descriptionRes, int _enabledRes) {
- super(_owner, _outError, _nameRes, _labelRes, _iconRes);
+ super(_owner, _outError, _nameRes, _labelRes, _iconRes, _logoRes);
sepProcesses = _sepProcesses;
processRes = _processRes;
descriptionRes = _descriptionRes;
@@ -789,6 +791,7 @@ public class PackageParser {
int supportsSmallScreens = 1;
int supportsNormalScreens = 1;
int supportsLargeScreens = 1;
+ int supportsXLargeScreens = 1;
int resizeable = 1;
int anyDensity = 1;
@@ -996,9 +999,12 @@ public class PackageParser {
supportsLargeScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
supportsLargeScreens);
+ supportsXLargeScreens = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens,
+ supportsXLargeScreens);
resizeable = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,
- supportsLargeScreens);
+ resizeable);
anyDensity = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,
anyDensity);
@@ -1132,6 +1138,11 @@ public class PackageParser {
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
}
+ if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
+ && pkg.applicationInfo.targetSdkVersion
+ >= android.os.Build.VERSION_CODES.GINGERBREAD)) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
+ }
if (resizeable < 0 || (resizeable > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
@@ -1241,7 +1252,8 @@ public class PackageParser {
"<permission-group>", sa,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_label,
- com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon)) {
+ com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon,
+ com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
@@ -1276,7 +1288,8 @@ public class PackageParser {
"<permission>", sa,
com.android.internal.R.styleable.AndroidManifestPermission_name,
com.android.internal.R.styleable.AndroidManifestPermission_label,
- com.android.internal.R.styleable.AndroidManifestPermission_icon)) {
+ com.android.internal.R.styleable.AndroidManifestPermission_icon,
+ com.android.internal.R.styleable.AndroidManifestPermission_logo)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
@@ -1329,7 +1342,8 @@ public class PackageParser {
"<permission-tree>", sa,
com.android.internal.R.styleable.AndroidManifestPermissionTree_name,
com.android.internal.R.styleable.AndroidManifestPermissionTree_label,
- com.android.internal.R.styleable.AndroidManifestPermissionTree_icon)) {
+ com.android.internal.R.styleable.AndroidManifestPermissionTree_icon,
+ com.android.internal.R.styleable.AndroidManifestPermissionTree_logo)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
@@ -1373,7 +1387,8 @@ public class PackageParser {
mParseInstrumentationArgs = new ParsePackageItemArgs(owner, outError,
com.android.internal.R.styleable.AndroidManifestInstrumentation_name,
com.android.internal.R.styleable.AndroidManifestInstrumentation_label,
- com.android.internal.R.styleable.AndroidManifestInstrumentation_icon);
+ com.android.internal.R.styleable.AndroidManifestInstrumentation_icon,
+ com.android.internal.R.styleable.AndroidManifestInstrumentation_logo);
mParseInstrumentationArgs.tag = "<instrumentation>";
}
@@ -1485,6 +1500,8 @@ public class PackageParser {
ai.icon = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
+ ai.logo = sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifestApplication_logo, 0);
ai.theme = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
ai.descriptionRes = sa.getResourceId(
@@ -1542,6 +1559,12 @@ public class PackageParser {
ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
}
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_neverEncrypt,
+ false)) {
+ ai.flags |= ApplicationInfo.FLAG_NEVER_ENCRYPT;
+ }
+
String str;
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
@@ -1577,6 +1600,18 @@ public class PackageParser {
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();
@@ -1705,7 +1740,7 @@ public class PackageParser {
private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,
String[] outError, String tag, TypedArray sa,
- int nameRes, int labelRes, int iconRes) {
+ int nameRes, int labelRes, int iconRes, int logoRes) {
String name = sa.getNonConfigurationString(nameRes, 0);
if (name == null) {
outError[0] = tag + " does not specify android:name";
@@ -1723,6 +1758,11 @@ public class PackageParser {
outInfo.icon = iconVal;
outInfo.nonLocalizedLabel = null;
}
+
+ int logoVal = sa.getResourceId(logoRes, 0);
+ if (logoVal != 0) {
+ outInfo.logo = logoVal;
+ }
TypedValue v = sa.peekValue(labelRes);
if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
@@ -1745,6 +1785,7 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestActivity_name,
com.android.internal.R.styleable.AndroidManifestActivity_label,
com.android.internal.R.styleable.AndroidManifestActivity_icon,
+ com.android.internal.R.styleable.AndroidManifestActivity_logo,
mSeparateProcesses,
com.android.internal.R.styleable.AndroidManifestActivity_process,
com.android.internal.R.styleable.AndroidManifestActivity_description,
@@ -1860,6 +1901,14 @@ public class PackageParser {
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;
}
@@ -1947,6 +1996,7 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestActivityAlias_name,
com.android.internal.R.styleable.AndroidManifestActivityAlias_label,
com.android.internal.R.styleable.AndroidManifestActivityAlias_icon,
+ com.android.internal.R.styleable.AndroidManifestActivityAlias_logo,
mSeparateProcesses,
0,
com.android.internal.R.styleable.AndroidManifestActivityAlias_description,
@@ -1980,6 +2030,7 @@ public class PackageParser {
info.configChanges = target.info.configChanges;
info.flags = target.info.flags;
info.icon = target.info.icon;
+ info.logo = target.info.logo;
info.labelRes = target.info.labelRes;
info.nonLocalizedLabel = target.info.nonLocalizedLabel;
info.launchMode = target.info.launchMode;
@@ -2074,6 +2125,7 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestProvider_name,
com.android.internal.R.styleable.AndroidManifestProvider_label,
com.android.internal.R.styleable.AndroidManifestProvider_icon,
+ com.android.internal.R.styleable.AndroidManifestProvider_logo,
mSeparateProcesses,
com.android.internal.R.styleable.AndroidManifestProvider_process,
com.android.internal.R.styleable.AndroidManifestProvider_description,
@@ -2139,6 +2191,15 @@ public class PackageParser {
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;
@@ -2337,6 +2398,7 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestService_name,
com.android.internal.R.styleable.AndroidManifestService_label,
com.android.internal.R.styleable.AndroidManifestService_icon,
+ com.android.internal.R.styleable.AndroidManifestService_logo,
mSeparateProcesses,
com.android.internal.R.styleable.AndroidManifestService_process,
com.android.internal.R.styleable.AndroidManifestService_description,
@@ -2370,6 +2432,15 @@ public class PackageParser {
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
@@ -2540,6 +2611,9 @@ public class PackageParser {
outInfo.icon = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0);
+
+ outInfo.logo = sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifestIntentFilter_logo, 0);
sa.recycle();
@@ -2801,6 +2875,11 @@ public class PackageParser {
outInfo.icon = iconVal;
outInfo.nonLocalizedLabel = null;
}
+
+ int logoVal = args.sa.getResourceId(args.logoRes, 0);
+ if (logoVal != 0) {
+ outInfo.logo = logoVal;
+ }
TypedValue v = args.sa.peekValue(args.labelRes);
if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
@@ -3135,6 +3214,7 @@ public class PackageParser {
public int labelRes;
public CharSequence nonLocalizedLabel;
public int icon;
+ public int logo;
}
public final static class ActivityIntentInfo extends IntentInfo {
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 11c67cc..d0ba590 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -99,7 +99,22 @@ public class CompatibilityInfo {
*/
private static final int CONFIGURED_LARGE_SCREENS = 16;
- private static final int SCALING_EXPANDABLE_MASK = SCALING_REQUIRED | EXPANDABLE | LARGE_SCREENS;
+ /**
+ * A flag mask to indicates that the application supports xlarge screens.
+ * The flag is set to true if
+ * 1) Application declares it supports xlarge screens in manifest file using <supports-screens> or
+ * 2) The screen size is not xlarge
+ * {@see compatibilityFlag}
+ */
+ private static final int XLARGE_SCREENS = 32;
+
+ /**
+ * A flag mask to tell if the application supports xlarge screens. This differs
+ * from XLARGE_SCREENS in that the application that does not support xlarge
+ * screens will be marked as supporting them if the current screen is not
+ * xlarge.
+ */
+ private static final int CONFIGURED_XLARGE_SCREENS = 64;
/**
* The effective screen density we have selected for this application.
@@ -127,6 +142,9 @@ public class CompatibilityInfo {
if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
mCompatibilityFlags |= LARGE_SCREENS | CONFIGURED_LARGE_SCREENS;
}
+ if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
+ mCompatibilityFlags |= XLARGE_SCREENS | CONFIGURED_XLARGE_SCREENS;
+ }
if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
mCompatibilityFlags |= EXPANDABLE | CONFIGURED_EXPANDABLE;
}
@@ -157,6 +175,7 @@ public class CompatibilityInfo {
this(ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
| ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
| ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
+ | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS
| ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS,
EXPANDABLE | CONFIGURED_EXPANDABLE,
DisplayMetrics.DENSITY_DEVICE,
@@ -196,6 +215,17 @@ public class CompatibilityInfo {
}
/**
+ * Sets large screen bit in the compatibility flag.
+ */
+ public void setXLargeScreens(boolean expandable) {
+ if (expandable) {
+ mCompatibilityFlags |= CompatibilityInfo.XLARGE_SCREENS;
+ } else {
+ mCompatibilityFlags &= ~CompatibilityInfo.XLARGE_SCREENS;
+ }
+ }
+
+ /**
* @return true if the application is configured to be expandable.
*/
public boolean isConfiguredExpandable() {
@@ -210,6 +240,13 @@ public class CompatibilityInfo {
}
/**
+ * @return true if the application is configured to be expandable.
+ */
+ public boolean isConfiguredXLargeScreens() {
+ return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_XLARGE_SCREENS) != 0;
+ }
+
+ /**
* @return true if the scaling is required
*/
public boolean isScalingRequired() {
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 1a0c867..02956ba 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -62,6 +62,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
public static final int SCREENLAYOUT_SIZE_SMALL = 0x01;
public static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
+ public static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
public static final int SCREENLAYOUT_LONG_MASK = 0x30;
public static final int SCREENLAYOUT_LONG_UNDEFINED = 0x00;
@@ -82,7 +83,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
* <p>The {@link #SCREENLAYOUT_SIZE_MASK} bits define the overall size
* of the screen. They may be one of
* {@link #SCREENLAYOUT_SIZE_SMALL}, {@link #SCREENLAYOUT_SIZE_NORMAL},
- * or {@link #SCREENLAYOUT_SIZE_LARGE}.
+ * {@link #SCREENLAYOUT_SIZE_LARGE}, or {@link #SCREENLAYOUT_SIZE_XLARGE}.
*
* <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen
* is wider/taller than normal. They may be one of
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index 79178f4..6539156 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -25,6 +25,9 @@ import java.util.Map;
/**
* This interface provides random read-write access to the result set returned
* by a database query.
+ *
+ * Cursor implementations are not required to be synchronized so code using a Cursor from multiple
+ * threads should perform its own synchronization when using the Cursor.
*/
public interface Cursor {
/**
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 6e5b3e1..c7e58fa 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -36,6 +36,9 @@ import java.util.concurrent.locks.ReentrantLock;
/**
* A Cursor implementation that exposes results from a query on a
* {@link SQLiteDatabase}.
+ *
+ * SQLiteCursor is not internally synchronized so code using a SQLiteCursor from multiple
+ * threads should perform its own synchronization when using the SQLiteCursor.
*/
public class SQLiteCursor extends AbstractWindowedCursor {
static final String TAG = "Cursor";
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index fb5507d..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.EventLog;
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;
@@ -1134,7 +1136,8 @@ public class SQLiteDatabase extends SQLiteClosable {
*
* @param sql The raw SQL statement, may contain ? for unknown values to be
* bound later.
- * @return a pre-compiled statement object.
+ * @return A pre-compiled {@link SQLiteStatement} object. Note that
+ * {@link SQLiteStatement}s are not synchronized, see the documentation for more details.
*/
public SQLiteStatement compileStatement(String sql) throws SQLException {
lock();
@@ -1175,7 +1178,8 @@ public class SQLiteDatabase extends SQLiteClosable {
* default sort order, which may be unordered.
* @param limit Limits the number of rows returned by the query,
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return A Cursor object, which is positioned before the first entry
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
* @see Cursor
*/
public Cursor query(boolean distinct, String table, String[] columns,
@@ -1213,7 +1217,8 @@ public class SQLiteDatabase extends SQLiteClosable {
* default sort order, which may be unordered.
* @param limit Limits the number of rows returned by the query,
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return A Cursor object, which is positioned before the first entry
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
* @see Cursor
*/
public Cursor queryWithFactory(CursorFactory cursorFactory,
@@ -1254,7 +1259,8 @@ public class SQLiteDatabase extends SQLiteClosable {
* @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
* (excluding the ORDER BY itself). Passing null will use the
* default sort order, which may be unordered.
- * @return A {@link Cursor} object, which is positioned before the first entry
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
* @see Cursor
*/
public Cursor query(String table, String[] columns, String selection,
@@ -1291,7 +1297,8 @@ public class SQLiteDatabase extends SQLiteClosable {
* default sort order, which may be unordered.
* @param limit Limits the number of rows returned by the query,
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return A {@link Cursor} object, which is positioned before the first entry
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
* @see Cursor
*/
public Cursor query(String table, String[] columns, String selection,
@@ -1309,7 +1316,8 @@ public class SQLiteDatabase extends SQLiteClosable {
* @param selectionArgs You may include ?s in where clause in the query,
* which will be replaced by the values from selectionArgs. The
* values will be bound as Strings.
- * @return A {@link Cursor} object, which is positioned before the first entry
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
*/
public Cursor rawQuery(String sql, String[] selectionArgs) {
return rawQueryWithFactory(null, sql, selectionArgs, null);
@@ -1324,7 +1332,8 @@ public class SQLiteDatabase extends SQLiteClosable {
* which will be replaced by the values from selectionArgs. The
* values will be bound as Strings.
* @param editTable the name of the first table, which is editable
- * @return A {@link Cursor} object, which is positioned before the first entry
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
*/
public Cursor rawQueryWithFactory(
CursorFactory cursorFactory, String sql, String[] selectionArgs,
@@ -1332,6 +1341,7 @@ public class SQLiteDatabase extends SQLiteClosable {
if (!isOpen()) {
throw new IllegalStateException("database not open");
}
+ BlockGuard.getThreadPolicy().onReadFromDisk();
long timeStart = 0;
if (Config.LOGV || mSlowQueryThreshold != -1) {
@@ -1379,7 +1389,8 @@ public class SQLiteDatabase extends SQLiteClosable {
* values will be bound as Strings.
* @param initialRead set the initial count of items to read from the cursor
* @param maxRead set the count of items to read on each iteration after the first
- * @return A {@link Cursor} object, which is positioned before the first entry
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
*
* This work is incomplete and not fully tested or reviewed, so currently
* hidden.
@@ -1489,6 +1500,7 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
public long insertWithOnConflict(String table, String nullColumnHack,
ContentValues initialValues, int conflictAlgorithm) {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
if (!isOpen()) {
throw new IllegalStateException("database not open");
}
@@ -1580,6 +1592,7 @@ public class SQLiteDatabase extends SQLiteClosable {
* whereClause.
*/
public int delete(String table, String whereClause, String[] whereArgs) {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
lock();
if (!isOpen()) {
throw new IllegalStateException("database not open");
@@ -1635,6 +1648,7 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
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");
}
@@ -1717,6 +1731,7 @@ public class SQLiteDatabase extends SQLiteClosable {
* @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()) {
@@ -1752,6 +1767,7 @@ public class SQLiteDatabase extends SQLiteClosable {
* @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/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 52aac3a..aefbabc 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -28,6 +28,12 @@ import android.util.Log;
* Transactions are used to make sure the database is always in a sensible state.
* <p>For an example, see the NotePadProvider class in the NotePad sample application,
* in the <em>samples/</em> directory of the SDK.</p>
+ *
+ * <p class="note"><strong>Note:</strong> this class assumes
+ * monotonically increasing version numbers for upgrades. Also, there
+ * is no concept of a database downgrade; installing a new version of
+ * your app which uses a lower version number than a
+ * previously-installed version will result in undefined behavior.</p>
*/
public abstract class SQLiteOpenHelper {
private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
@@ -105,6 +111,10 @@ public abstract class SQLiteOpenHelper {
if (version == 0) {
onCreate(db);
} else {
+ if (version > mNewVersion) {
+ Log.wtf(TAG, "Can't downgrade read-only database from version " +
+ version + " to " + mNewVersion + ": " + db.getPath());
+ }
onUpgrade(db, version, mNewVersion);
}
db.setVersion(mNewVersion);
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 89a5f0d..4d96f12 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -20,6 +20,9 @@ import android.util.Log;
/**
* A base class for compiled SQLite programs.
+ *
+ * SQLiteProgram is not internally synchronized so code using a SQLiteProgram from multiple
+ * threads should perform its own synchronization when using the SQLiteProgram.
*/
public abstract class SQLiteProgram extends SQLiteClosable {
diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java
index 43d2fac..905b66b 100644
--- a/core/java/android/database/sqlite/SQLiteQuery.java
+++ b/core/java/android/database/sqlite/SQLiteQuery.java
@@ -23,6 +23,9 @@ import android.util.Log;
/**
* A SQLite program that represents a query that reads the resulting rows into a CursorWindow.
* This class is used by SQLiteCursor and isn't useful itself.
+ *
+ * SQLiteQuery is not internally synchronized so code using a SQLiteQuery from multiple
+ * threads should perform its own synchronization when using the SQLiteQuery.
*/
public class SQLiteQuery extends SQLiteProgram {
private static final String TAG = "Cursor";
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 98da414..9e425c3 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -18,11 +18,16 @@ package android.database.sqlite;
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.
* Don't use SQLiteStatement constructor directly, please use
* {@link SQLiteDatabase#compileStatement(String)}
+ *
+ * SQLiteStatement is not internally synchronized so code using a SQLiteStatement from multiple
+ * threads should perform its own synchronization when using the SQLiteStatement.
*/
public class SQLiteStatement extends SQLiteProgram
{
@@ -44,6 +49,7 @@ public class SQLiteStatement extends SQLiteProgram
* some reason
*/
public void execute() {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
@@ -70,6 +76,7 @@ public class SQLiteStatement extends SQLiteProgram
* some reason
*/
public long executeInsert() {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
@@ -96,6 +103,7 @@ public class SQLiteStatement extends SQLiteProgram
* @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");
}
@@ -122,6 +130,7 @@ public class SQLiteStatement extends SQLiteProgram
* @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 8687a89..7640cc1 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -58,7 +58,7 @@ import android.os.Message;
public class Camera {
private static final String TAG = "Camera";
- // These match the enums in frameworks/base/include/ui/Camera.h
+ // These match the enums in frameworks/base/include/camera/Camera.h
private static final int CAMERA_MSG_ERROR = 0x001;
private static final int CAMERA_MSG_SHUTTER = 0x002;
private static final int CAMERA_MSG_FOCUS = 0x004;
@@ -84,13 +84,71 @@ public class Camera {
private boolean mWithBuffer;
/**
+ * Returns the number of Cameras available.
+ * @hide
+ */
+ public native static int getNumberOfCameras();
+
+ /**
+ * Returns the information about the camera.
+ * If {@link #getNumberOfCameras()} returns N, the valid id is 0 to N-1.
+ * @hide
+ */
+ public native static void getCameraInfo(int cameraId, CameraInfo cameraInfo);
+
+ /**
+ * Information about a camera
+ * @hide
+ */
+ public static class CameraInfo {
+ public static final int CAMERA_FACING_BACK = 0;
+ public static final int CAMERA_FACING_FRONT = 1;
+
+ /**
+ * The direction that the camera faces to. It should be
+ * CAMERA_FACING_BACK or CAMERA_FACING_FRONT.
+ */
+ public int mFacing;
+
+ /**
+ * The orientation of the camera image. The value is the angle that the
+ * camera image needs to be rotated clockwise so it shows correctly on
+ * the display in its natural orientation. It should be 0, 90, 180, or 270.
+ *
+ * For example, suppose a device has a naturally tall screen, but the camera
+ * sensor is mounted in landscape. If the top side of the camera sensor is
+ * aligned with the right edge of the display in natural orientation, the
+ * value should be 90.
+ *
+ * @see #setDisplayOrientation(int)
+ */
+ public int mOrientation;
+ };
+
+ /**
* Returns a new Camera object.
+ * If {@link #getNumberOfCameras()} returns N, the valid id is 0 to N-1.
+ * The id 0 is the default camera.
+ * @hide
+ */
+ public static Camera open(int cameraId) {
+ return new Camera(cameraId);
+ }
+
+ /**
+ * The id for the default camera.
+ * @hide
+ */
+ public static int CAMERA_ID_DEFAULT = 0;
+
+ /**
+ * Returns a new Camera object. This returns the default camera.
*/
public static Camera open() {
- return new Camera();
+ return new Camera(CAMERA_ID_DEFAULT);
}
- Camera() {
+ Camera(int cameraId) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
@@ -107,14 +165,14 @@ public class Camera {
mEventHandler = null;
}
- native_setup(new WeakReference<Camera>(this));
+ native_setup(new WeakReference<Camera>(this), cameraId);
}
protected void finalize() {
native_release();
}
- private native final void native_setup(Object camera_this);
+ private native final void native_setup(Object camera_this, int cameraId);
private native final void native_release();
@@ -565,6 +623,18 @@ public class Camera {
* {@link PreviewCallback#onPreviewFrame}. This method is not allowed to
* be called during preview.
*
+ * If you want to make the camera image show in the same orientation as
+ * the display, you can use <p>
+ * <pre>
+ * android.view.Display display;
+ * android.hardware.Camera.CameraInfo cameraInfo;
+ *
+ * int rotation = getWindowManager().getDefaultDisplay().getRotation();
+ * android.hardware.Camera.getCameraInfo(id, cameraInfo);
+ * int degrees = (cameraInfo.mOrientation - rotation + 360) % 360;
+ *
+ * setDisplayOrientation(degrees);
+ * </pre>
* @param degrees the angle that the picture will be rotated clockwise.
* Valid values are 0, 90, 180, and 270. The starting
* position is 0 (landscape).
@@ -746,6 +816,9 @@ public class Camera {
private static final String KEY_ZOOM_RATIOS = "zoom-ratios";
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";
@@ -807,21 +880,81 @@ public class Camera {
*/
public static final String FLASH_MODE_TORCH = "torch";
- // Values for scene mode settings.
+ /**
+ * Scene mode is off.
+ */
public static final String SCENE_MODE_AUTO = "auto";
+
+ /**
+ * Take photos of fast moving objects. Same as {@link
+ * #SCENE_MODE_SPORTS}.
+ */
public static final String SCENE_MODE_ACTION = "action";
+
+ /**
+ * Take people pictures.
+ */
public static final String SCENE_MODE_PORTRAIT = "portrait";
+
+ /**
+ * Take pictures on distant objects.
+ */
public static final String SCENE_MODE_LANDSCAPE = "landscape";
+
+ /**
+ * Take photos at night.
+ */
public static final String SCENE_MODE_NIGHT = "night";
+
+ /**
+ * Take people pictures at night.
+ */
public static final String SCENE_MODE_NIGHT_PORTRAIT = "night-portrait";
+
+ /**
+ * Take photos in a theater. Flash light is off.
+ */
public static final String SCENE_MODE_THEATRE = "theatre";
+
+ /**
+ * Take pictures on the beach.
+ */
public static final String SCENE_MODE_BEACH = "beach";
+
+ /**
+ * Take pictures on the snow.
+ */
public static final String SCENE_MODE_SNOW = "snow";
+
+ /**
+ * Take sunset photos.
+ */
public static final String SCENE_MODE_SUNSET = "sunset";
+
+ /**
+ * Avoid blurry pictures (for example, due to hand shake).
+ */
public static final String SCENE_MODE_STEADYPHOTO = "steadyphoto";
+
+ /**
+ * For shooting firework displays.
+ */
public static final String SCENE_MODE_FIREWORKS = "fireworks";
+
+ /**
+ * Take photos of fast moving objects. Same as {@link
+ * #SCENE_MODE_ACTION}.
+ */
public static final String SCENE_MODE_SPORTS = "sports";
+
+ /**
+ * Take indoor low-light shot.
+ */
public static final String SCENE_MODE_PARTY = "party";
+
+ /**
+ * Capture the naturally warm color of scenes lit by candles.
+ */
public static final String SCENE_MODE_CANDLELIGHT = "candlelight";
/**
@@ -858,6 +991,53 @@ public class Camera {
*/
public static final String FOCUS_MODE_EDOF = "edof";
+ // Indices for focus distance array.
+ /**
+ * The array index of near focus distance for use with
+ * {@link #getFocusDistances(float[])}.
+ */
+ public static final int FOCUS_DISTANCE_NEAR_INDEX = 0;
+
+ /**
+ * The array index of optimal focus distance for use with
+ * {@link #getFocusDistances(float[])}.
+ */
+ public static final int FOCUS_DISTANCE_OPTIMAL_INDEX = 1;
+
+ /**
+ * The array index of far focus distance for use with
+ * {@link #getFocusDistances(float[])}.
+ */
+ public static final int FOCUS_DISTANCE_FAR_INDEX = 2;
+
+ /**
+ * Continuous focus mode. The camera continuously tries to focus. This
+ * is ideal for shooting video or shooting photo of moving object.
+ * Continuous focus starts when {@link #autoFocus(AutoFocusCallback)} is
+ * called. Continuous focus stops when {@link #cancelAutoFocus()} is
+ * called. AutoFocusCallback will be only called once as soon as the
+ * picture is in focus.
+ */
+ public static final String FOCUS_MODE_CONTINUOUS = "continuous";
+
+ /**
+ * The camera determines the exposure by giving more weight to the
+ * central part of the scene.
+ */
+ 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.
+ */
+ 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.
+ */
+ public static final String METERING_MODE_SPOT = "spot";
+
// Formats for setPreviewFormat and setPictureFormat.
private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp";
private static final String PIXEL_FORMAT_YUV420SP = "yuv420sp";
@@ -1788,6 +1968,76 @@ public class Camera {
return TRUE.equals(str);
}
+ /**
+ * Gets the distances from the camera to where an object appears to be
+ * in focus. The object is sharpest at the optimal focus distance. The
+ * depth of field is the far focus distance minus near focus distance.
+ *
+ * Focus distances may change after calling {@link
+ * #autoFocus(AutoFocusCallback)}, {@link #cancelAutoFocus}, or {@link
+ * #startPreview()}. Applications can call {@link #getParameters()}
+ * and this method anytime to get the latest focus distances. If the
+ * focus mode is FOCUS_MODE_CONTINUOUS and autofocus has started, focus
+ * distances may change from time to time.
+ *
+ * Far focus distance >= optimal focus distance >= near focus distance.
+ * If the focus distance is infinity, the value will be
+ * Float.POSITIVE_INFINITY.
+ *
+ * @param output focus distances in meters. output must be a float
+ * array with three elements. Near focus distance, optimal focus
+ * distance, and far focus distance will be filled in the array.
+ * @see #FOCUS_DISTANCE_NEAR_INDEX
+ * @see #FOCUS_DISTANCE_OPTIMAL_INDEX
+ * @see #FOCUS_DISTANCE_FAR_INDEX
+ */
+ public void getFocusDistances(float[] output) {
+ if (output == null || output.length != 3) {
+ throw new IllegalArgumentException(
+ "output must be an float array with three elements.");
+ }
+ List<Float> distances = splitFloat(get(KEY_FOCUS_DISTANCES));
+ output[0] = distances.get(0);
+ output[1] = distances.get(1);
+ 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()
+ */
+ 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
+ */
+ public String getMeteringMode() {
+ return get(KEY_METERING_MODE);
+ }
+
+ /**
+ * Sets the metering mode.
+ *
+ * @param value metering mode.
+ * @see #getMeteringMode()
+ */
+ 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) {
@@ -1817,6 +2067,21 @@ public class Camera {
return substrings;
}
+ // Splits a comma delimited string to an ArrayList of Float.
+ // Return null if the passing string is null or the size is 0.
+ private ArrayList<Float> splitFloat(String str) {
+ if (str == null) return null;
+
+ StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ ArrayList<Float> substrings = new ArrayList<Float>();
+ while (tokenizer.hasMoreElements()) {
+ String token = tokenizer.nextToken();
+ substrings.add(Float.parseFloat(token));
+ }
+ if (substrings.size() == 0) return null;
+ return substrings;
+ }
+
// Returns the value of a float parameter.
private float getFloat(String key, float defaultValue) {
try {
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 80e9865..44f30f7 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -47,9 +47,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
private static final int DO_UPDATE_CURSOR = 95;
private static final int DO_APP_PRIVATE_COMMAND = 100;
private static final int DO_TOGGLE_SOFT_INPUT = 105;
-
- final HandlerCaller mCaller;
- final InputMethodSession mInputMethodSession;
+ private static final int DO_FINISH_SESSION = 110;
+
+ HandlerCaller mCaller;
+ InputMethodSession mInputMethodSession;
// NOTE: we should have a cache of these.
static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback {
@@ -127,6 +128,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
mInputMethodSession.toggleSoftInput(msg.arg1, msg.arg2);
return;
}
+ case DO_FINISH_SESSION: {
+ mInputMethodSession = null;
+ return;
+ }
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -174,4 +179,8 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
public void toggleSoftInput(int showFlags, int hideFlags) {
mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
}
+
+ public void finishSession() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION));
+ }
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index bfa82ee..35fd46f 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -39,6 +39,7 @@ import android.view.inputmethod.InputMethodSession;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -64,9 +65,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_SHOW_SOFT_INPUT = 60;
private static final int DO_HIDE_SOFT_INPUT = 70;
- final AbstractInputMethodService mTarget;
+ final WeakReference<AbstractInputMethodService> mTarget;
final HandlerCaller mCaller;
- final InputMethod mInputMethod;
+ final WeakReference<InputMethod> mInputMethod;
static class Notifier {
boolean notified;
@@ -96,21 +97,32 @@ class IInputMethodWrapper extends IInputMethod.Stub
public IInputMethodWrapper(AbstractInputMethodService context,
InputMethod inputMethod) {
- mTarget = context;
- mCaller = new HandlerCaller(context, this);
- mInputMethod = inputMethod;
+ mTarget = new WeakReference<AbstractInputMethodService>(context);
+ mCaller = new HandlerCaller(context.getApplicationContext(), this);
+ mInputMethod = new WeakReference<InputMethod>(inputMethod);
}
public InputMethod getInternalInputMethod() {
- return mInputMethod;
+ return mInputMethod.get();
}
public void executeMessage(Message msg) {
+ InputMethod inputMethod = mInputMethod.get();
+ // Need a valid reference to the inputMethod for everything except a dump.
+ if (inputMethod == null && msg.what != DO_DUMP) {
+ Log.w(TAG, "Input method reference was null, ignoring message: " + msg.what);
+ return;
+ }
+
switch (msg.what) {
case DO_DUMP: {
+ AbstractInputMethodService target = mTarget.get();
+ if (target == null) {
+ return;
+ }
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
try {
- mTarget.dump((FileDescriptor)args.arg1,
+ target.dump((FileDescriptor)args.arg1,
(PrintWriter)args.arg2, (String[])args.arg3);
} catch (RuntimeException e) {
((PrintWriter)args.arg2).println("Exception: " + e);
@@ -122,22 +134,22 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
case DO_ATTACH_TOKEN: {
- mInputMethod.attachToken((IBinder)msg.obj);
+ inputMethod.attachToken((IBinder)msg.obj);
return;
}
case DO_SET_INPUT_CONTEXT: {
- mInputMethod.bindInput((InputBinding)msg.obj);
+ inputMethod.bindInput((InputBinding)msg.obj);
return;
}
case DO_UNSET_INPUT_CONTEXT:
- mInputMethod.unbindInput();
+ inputMethod.unbindInput();
return;
case DO_START_INPUT: {
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
IInputContext inputContext = (IInputContext)args.arg1;
InputConnection ic = inputContext != null
? new InputConnectionWrapper(inputContext) : null;
- mInputMethod.startInput(ic, (EditorInfo)args.arg2);
+ inputMethod.startInput(ic, (EditorInfo)args.arg2);
return;
}
case DO_RESTART_INPUT: {
@@ -145,33 +157,37 @@ class IInputMethodWrapper extends IInputMethod.Stub
IInputContext inputContext = (IInputContext)args.arg1;
InputConnection ic = inputContext != null
? new InputConnectionWrapper(inputContext) : null;
- mInputMethod.restartInput(ic, (EditorInfo)args.arg2);
+ inputMethod.restartInput(ic, (EditorInfo)args.arg2);
return;
}
case DO_CREATE_SESSION: {
- mInputMethod.createSession(new InputMethodSessionCallbackWrapper(
+ inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mCaller.mContext, (IInputMethodCallback)msg.obj));
return;
}
case DO_SET_SESSION_ENABLED:
- mInputMethod.setSessionEnabled((InputMethodSession)msg.obj,
+ inputMethod.setSessionEnabled((InputMethodSession)msg.obj,
msg.arg1 != 0);
return;
case DO_REVOKE_SESSION:
- mInputMethod.revokeSession((InputMethodSession)msg.obj);
+ inputMethod.revokeSession((InputMethodSession)msg.obj);
return;
case DO_SHOW_SOFT_INPUT:
- mInputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj);
+ inputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj);
return;
case DO_HIDE_SOFT_INPUT:
- mInputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
+ inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
return;
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
- if (mTarget.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ AbstractInputMethodService target = mTarget.get();
+ if (target == null) {
+ return;
+ }
+ if (target.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
fout.println("Permission Denial: can't dump InputMethodManager from from pid="
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 8c8d3e5..1a261d3 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1988,15 +1988,19 @@ public class InputMethodService extends AbstractInputMethodService {
ei.inputType != InputType.TYPE_NULL);
if (hasAction) {
mExtractAccessories.setVisibility(View.VISIBLE);
- if (ei.actionLabel != null) {
- mExtractAction.setText(ei.actionLabel);
- } else {
- mExtractAction.setText(getTextForImeAction(ei.imeOptions));
+ if (mExtractAction != null) {
+ if (ei.actionLabel != null) {
+ mExtractAction.setText(ei.actionLabel);
+ } else {
+ mExtractAction.setText(getTextForImeAction(ei.imeOptions));
+ }
+ mExtractAction.setOnClickListener(mActionClickListener);
}
- mExtractAction.setOnClickListener(mActionClickListener);
} else {
mExtractAccessories.setVisibility(View.GONE);
- mExtractAction.setOnClickListener(null);
+ if (mExtractAction != null) {
+ mExtractAction.setOnClickListener(null);
+ }
}
}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 98f32b3..214510d 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -310,6 +310,9 @@ public class MobileDataStateTracker extends NetworkStateTracker {
case TelephonyManager.NETWORK_TYPE_EVDO_A:
networkTypeStr = "evdo";
break;
+ case TelephonyManager.NETWORK_TYPE_EVDO_B:
+ networkTypeStr = "evdo";
+ break;
}
return "net.tcp.buffersize." + networkTypeStr;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index d114bff..4adeaeb 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -51,50 +51,36 @@ public abstract class BatteryStats implements Parcelable {
/**
* A constant indicating a sensor timer.
- *
- * {@hide}
*/
public static final int SENSOR = 3;
/**
* A constant indicating a a wifi turn on timer
- *
- * {@hide}
*/
public static final int WIFI_TURNED_ON = 4;
/**
* A constant indicating a full wifi lock timer
- *
- * {@hide}
*/
public static final int FULL_WIFI_LOCK = 5;
/**
* A constant indicating a scan wifi lock timer
- *
- * {@hide}
*/
public static final int SCAN_WIFI_LOCK = 6;
/**
* A constant indicating a wifi multicast timer
- *
- * {@hide}
*/
public static final int WIFI_MULTICAST_ENABLED = 7;
/**
* A constant indicating an audio turn on timer
- *
- * {@hide}
*/
public static final int AUDIO_TURNED_ON = 7;
/**
* A constant indicating a video turn on timer
- *
- * {@hide}
*/
public static final int VIDEO_TURNED_ON = 8;
@@ -391,6 +377,61 @@ public abstract class BatteryStats implements Parcelable {
}
}
+ public final class BatteryHistoryRecord implements Parcelable {
+ public BatteryHistoryRecord next;
+
+ public long time;
+ public byte batteryLevel;
+
+ public static final int STATE_SCREEN_MASK = 0x000000f;
+ public static final int STATE_SCREEN_SHIFT = 0;
+ public static final int STATE_SIGNAL_STRENGTH_MASK = 0x00000f0;
+ public static final int STATE_SIGNAL_STRENGTH_SHIFT = 4;
+ public static final int STATE_PHONE_STATE_MASK = 0x0000f00;
+ public static final int STATE_PHONE_STATE_SHIFT = 8;
+ public static final int STATE_DATA_CONNECTION_MASK = 0x000f000;
+ public static final int STATE_DATA_CONNECTION_SHIFT = 12;
+
+ public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<30;
+ public static final int STATE_SCREEN_ON_FLAG = 1<<29;
+ public static final int STATE_GPS_ON_FLAG = 1<<28;
+ public static final int STATE_PHONE_ON_FLAG = 1<<27;
+ public static final int STATE_WIFI_ON_FLAG = 1<<26;
+ public static final int STATE_WIFI_RUNNING_FLAG = 1<<25;
+ public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<24;
+ public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<23;
+ public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<22;
+ public static final int STATE_BLUETOOTH_ON_FLAG = 1<<21;
+ public static final int STATE_AUDIO_ON_FLAG = 1<<20;
+ public static final int STATE_VIDEO_ON_FLAG = 1<<19;
+
+ public int states;
+
+ public BatteryHistoryRecord() {
+ }
+
+ public BatteryHistoryRecord(long time, Parcel src) {
+ this.time = time;
+ batteryLevel = (byte)src.readInt();
+ states = src.readInt();
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(time);
+ dest.writeInt(batteryLevel);
+ dest.writeInt(states);
+ }
+ }
+
+ /**
+ * Return the current history of battery state changes.
+ */
+ public abstract BatteryHistoryRecord getHistory();
+
/**
* Returns the number of times the device has been started.
*/
@@ -1443,6 +1484,20 @@ public abstract class BatteryStats implements Parcelable {
*/
@SuppressWarnings("unused")
public void dumpLocked(PrintWriter pw) {
+ BatteryHistoryRecord rec = getHistory();
+ if (rec != null) {
+ pw.println("Battery History:");
+ while (rec != null) {
+ pw.print(" ");
+ pw.print(rec.time);
+ pw.print(" ");
+ pw.print(rec.batteryLevel);
+ pw.print(" ");
+ pw.println(Integer.toHexString(rec.states));
+ rec = rec.next;
+ }
+ }
+
pw.println("Total Statistics (Current and Historic):");
pw.println(" System starts: " + getStartCount()
+ ", currently on battery: " + getIsOnBattery());
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 3e9fd42..9d1a634 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -180,6 +180,10 @@ public class Build {
public static final int ECLAIR_MR1 = 7;
public static final int FROYO = 8;
+
+ public static final int KRAKEN = CUR_DEVELOPMENT;
+
+ public static final int GINGERBREAD = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 812391c..c7cbed6 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -28,6 +28,8 @@ public class Environment {
private static final File ROOT_DIRECTORY
= getDirectory("ANDROID_ROOT", "/system");
+ private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
+
private static IMountService mMntSvc = null;
/**
@@ -37,9 +39,55 @@ public class Environment {
return ROOT_DIRECTORY;
}
+ /**
+ * Gets the system directory available for secure storage.
+ * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).
+ * Otherwise, it returns the unencrypted /data/system directory.
+ * @return File object representing the secure storage system directory.
+ * @hide
+ */
+ public static File getSystemSecureDirectory() {
+ if (isEncryptedFilesystemEnabled()) {
+ return new File(SECURE_DATA_DIRECTORY, "system");
+ } else {
+ return new File(DATA_DIRECTORY, "system");
+ }
+ }
+
+ /**
+ * Gets the data directory for secure storage.
+ * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure).
+ * Otherwise, it returns the unencrypted /data directory.
+ * @return File object representing the data directory for secure storage.
+ * @hide
+ */
+ public static File getSecureDataDirectory() {
+ if (isEncryptedFilesystemEnabled()) {
+ return SECURE_DATA_DIRECTORY;
+ } else {
+ return DATA_DIRECTORY;
+ }
+ }
+
+ /**
+ * Returns whether the Encrypted File System feature is enabled on the device or not.
+ * @return <code>true</code> if Encrypted File System feature is enabled, <code>false</code>
+ * if disabled.
+ * @hide
+ */
+ public static boolean isEncryptedFilesystemEnabled() {
+ return SystemProperties.getBoolean(SYSTEM_PROPERTY_EFS_ENABLED, false);
+ }
+
private static final File DATA_DIRECTORY
= getDirectory("ANDROID_DATA", "/data");
+ /**
+ * @hide
+ */
+ private static final File SECURE_DATA_DIRECTORY
+ = getDirectory("ANDROID_SECURE_DATA", "/data/secure");
+
private static final File EXTERNAL_STORAGE_DIRECTORY
= getDirectory("EXTERNAL_STORAGE", "/sdcard");
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index bc653d6..d394a46 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -36,8 +36,9 @@ public class MessageQueue {
Message mMessages;
private final ArrayList mIdleHandlers = new ArrayList();
private boolean mQuiting = false;
+ private int mObject = 0; // used by native code
boolean mQuitAllowed = true;
-
+
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
@@ -85,16 +86,49 @@ public class MessageQueue {
}
}
+ // Add an input pipe to the set being selected over. If token is
+ // negative, remove 'handler's entry from the current set and forget
+ // about it.
+ void setInputToken(int token, int region, Handler handler) {
+ if (token >= 0) nativeRegisterInputStream(token, region, handler);
+ else nativeUnregisterInputStream(token);
+ }
+
MessageQueue() {
+ nativeInit();
}
+ private native void nativeInit();
+
+ /**
+ * @param token fd of the readable end of the input stream
+ * @param region fd of the ashmem region used for data transport alongside the 'token' fd
+ * @param handler Handler from which to make input messages based on data read from the fd
+ */
+ private native void nativeRegisterInputStream(int token, int region, Handler handler);
+ private native void nativeUnregisterInputStream(int token);
+ private native void nativeSignal();
+
+ /**
+ * Wait until the designated time for new messages to arrive.
+ *
+ * @param when Timestamp in SystemClock.uptimeMillis() base of the next message in the queue.
+ * If 'when' is zero, the method will check for incoming messages without blocking. If
+ * 'when' is negative, the method will block forever waiting for the next message.
+ * @return
+ */
+ private native int nativeWaitForNext(long when);
final Message next() {
boolean tryIdle = true;
+ // when we start out, we'll just touch the input pipes and then go from there
+ long timeToNextEventMillis = 0;
while (true) {
long now;
Object[] idlers = null;
-
+
+ nativeWaitForNext(timeToNextEventMillis);
+
// Try to retrieve the next message, returning if found.
synchronized (this) {
now = SystemClock.uptimeMillis();
@@ -135,20 +169,17 @@ public class MessageQueue {
synchronized (this) {
// No messages, nobody to tell about it... time to wait!
- try {
- if (mMessages != null) {
- if (mMessages.when-now > 0) {
- Binder.flushPendingCommands();
- this.wait(mMessages.when-now);
- }
- } else {
+ if (mMessages != null) {
+ if (mMessages.when - now > 0) {
Binder.flushPendingCommands();
- this.wait();
+ timeToNextEventMillis = mMessages.when - now;
}
- }
- catch (InterruptedException e) {
+ } else {
+ Binder.flushPendingCommands();
+ timeToNextEventMillis = -1;
}
}
+ // loop to the while(true) and do the appropriate nativeWait(when)
}
}
@@ -190,7 +221,6 @@ public class MessageQueue {
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
- this.notify();
} else {
Message prev = null;
while (p != null && p.when <= when) {
@@ -199,8 +229,8 @@ public class MessageQueue {
}
msg.next = prev.next;
prev.next = msg;
- this.notify();
}
+ nativeSignal();
}
return true;
}
@@ -321,7 +351,7 @@ public class MessageQueue {
void poke()
{
synchronized (this) {
- this.notify();
+ nativeSignal();
}
}
}
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 0a3b2cf..d26f066 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -179,7 +179,7 @@ public class ParcelFileDescriptor implements Parcelable {
/**
* An InputStream you can create on a ParcelFileDescriptor, which will
* take care of calling {@link ParcelFileDescriptor#close
- * ParcelFileDescritor.close()} for you when the stream is closed.
+ * ParcelFileDescriptor.close()} for you when the stream is closed.
*/
public static class AutoCloseInputStream extends FileInputStream {
private final ParcelFileDescriptor mFd;
@@ -198,7 +198,7 @@ public class ParcelFileDescriptor implements Parcelable {
/**
* An OutputStream you can create on a ParcelFileDescriptor, which will
* take care of calling {@link ParcelFileDescriptor#close
- * ParcelFileDescritor.close()} for you when the stream is closed.
+ * ParcelFileDescriptor.close()} for you when the stream is closed.
*/
public static class AutoCloseOutputStream extends FileOutputStream {
private final ParcelFileDescriptor mFd;
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index b6dc1b5..5fea6fe 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -348,6 +348,23 @@ public class RecoverySystem {
}
/**
+ * Reboot into the recovery system to wipe the /data partition and toggle
+ * Encrypted File Systems on/off.
+ * @param extras to add to the RECOVERY_COMPLETED intent after rebooting.
+ * @throws IOException if something goes wrong.
+ *
+ * @hide
+ */
+ public static void rebootToggleEFS(Context context, boolean efsEnabled)
+ throws IOException {
+ if (efsEnabled) {
+ bootCommand(context, "--set_encrypted_filesystem=on");
+ } else {
+ bootCommand(context, "--set_encrypted_filesystem=off");
+ }
+ }
+
+ /**
* Reboot into the recovery system with the supplied argument.
* @param arg to pass to the recovery utility.
* @throws IOException if something goes wrong.
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
new file mode 100644
index 0000000..876ec39
--- /dev/null
+++ b/core/java/android/os/StrictMode.java
@@ -0,0 +1,219 @@
+/*
+ * 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 android.os;
+
+import android.app.ActivityManagerNative;
+import android.app.ApplicationErrorReport;
+import android.util.Log;
+
+import com.android.internal.os.RuntimeInit;
+
+import dalvik.system.BlockGuard;
+
+/**
+ * <p>StrictMode lets you impose stricter rules under which your
+ * application runs.</p>
+ */
+public final class StrictMode {
+ private static final String TAG = "StrictMode";
+
+ private StrictMode() {}
+
+ public static final int DISALLOW_DISK_WRITE = 0x01;
+ public static final int DISALLOW_DISK_READ = 0x02;
+ public static final int DISALLOW_NETWORK = 0x04;
+
+ /** @hide */
+ public static final int DISALLOW_MASK =
+ DISALLOW_DISK_WRITE | DISALLOW_DISK_READ | DISALLOW_NETWORK;
+
+ /**
+ * Flag to log to the system log.
+ */
+ public static final int PENALTY_LOG = 0x10; // normal android.util.Log
+
+ /**
+ * Show an annoying dialog to the user. Will be rate-limited to be only
+ * a little annoying.
+ */
+ public static final int PENALTY_DIALOG = 0x20;
+
+ /**
+ * Crash hard if policy is violated.
+ */
+ public static final int PENALTY_DEATH = 0x40;
+
+ /**
+ * Log a stacktrace to the DropBox on policy violation.
+ */
+ public static final int PENALTY_DROPBOX = 0x80;
+
+ /** @hide */
+ public static final int PENALTY_MASK =
+ PENALTY_LOG | PENALTY_DIALOG |
+ PENALTY_DROPBOX | PENALTY_DEATH;
+
+ /**
+ * Sets the policy for what actions the current thread is denied,
+ * as well as the penalty for violating the policy.
+ *
+ * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values.
+ */
+ public static void setThreadBlockingPolicy(final int policyMask) {
+ BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+ if (!(policy instanceof AndroidBlockGuardPolicy)) {
+ BlockGuard.setThreadPolicy(new AndroidBlockGuardPolicy(policyMask));
+ } else {
+ AndroidBlockGuardPolicy androidPolicy = (AndroidBlockGuardPolicy) policy;
+ androidPolicy.setPolicyMask(policyMask);
+ }
+ }
+
+ /**
+ * Returns the bitmask of the current thread's blocking policy.
+ *
+ * @return the bitmask of all the DISALLOW_* and PENALTY_* bits currently enabled
+ */
+ public static int getThreadBlockingPolicy() {
+ return BlockGuard.getThreadPolicy().getPolicyMask();
+ }
+
+ /** @hide */
+ public static void setDropBoxManager(DropBoxManager dropBoxManager) {
+ BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+ if (!(policy instanceof AndroidBlockGuardPolicy)) {
+ policy = new AndroidBlockGuardPolicy(0);
+ BlockGuard.setThreadPolicy(policy);
+ }
+ ((AndroidBlockGuardPolicy) policy).setDropBoxManager(dropBoxManager);
+ }
+
+ private static class AndroidBlockGuardPolicy implements BlockGuard.Policy {
+ private int mPolicyMask;
+ private DropBoxManager mDropBoxManager = null;
+
+ public AndroidBlockGuardPolicy(final int policyMask) {
+ mPolicyMask = policyMask;
+ }
+
+ // Part of BlockGuard.Policy interface:
+ public int getPolicyMask() {
+ return mPolicyMask;
+ }
+
+ // Part of BlockGuard.Policy interface:
+ public void onWriteToDisk() {
+ if ((mPolicyMask & DISALLOW_DISK_WRITE) == 0) {
+ return;
+ }
+ handleViolation(DISALLOW_DISK_WRITE);
+ }
+
+ // Part of BlockGuard.Policy interface:
+ public void onReadFromDisk() {
+ if ((mPolicyMask & DISALLOW_DISK_READ) == 0) {
+ return;
+ }
+ handleViolation(DISALLOW_DISK_READ);
+ }
+
+ // Part of BlockGuard.Policy interface:
+ public void onNetwork() {
+ if ((mPolicyMask & DISALLOW_NETWORK) == 0) {
+ return;
+ }
+ handleViolation(DISALLOW_NETWORK);
+ }
+
+ public void setPolicyMask(int policyMask) {
+ mPolicyMask = policyMask;
+ }
+
+ public void setDropBoxManager(DropBoxManager dropBoxManager) {
+ mDropBoxManager = dropBoxManager;
+ }
+
+ private void handleViolation(int violationBit) {
+ final BlockGuard.BlockGuardPolicyException violation =
+ new BlockGuard.BlockGuardPolicyException(mPolicyMask, violationBit);
+ violation.fillInStackTrace();
+
+ Looper looper = Looper.myLooper();
+ if (looper == null) {
+ // Without a Looper, we're unable to time how long the
+ // violation takes place. This case should be rare,
+ // as most users will care about timing violations
+ // that happen on their main UI thread.
+ handleViolationWithTime(violation, -1L /* no time */);
+ } else {
+ MessageQueue queue = Looper.myQueue();
+ final long violationTime = SystemClock.uptimeMillis();
+ queue.addIdleHandler(new MessageQueue.IdleHandler() {
+ public boolean queueIdle() {
+ long afterViolationTime = SystemClock.uptimeMillis();
+ handleViolationWithTime(violation, afterViolationTime - violationTime);
+ return false; // remove this idle handler from the array
+ }
+ });
+ }
+ }
+
+ private void handleViolationWithTime(
+ BlockGuard.BlockGuardPolicyException violation,
+ long durationMillis) {
+
+ // It's possible (even quite likely) that mPolicyMask has
+ // changed from the time the violation fired and now
+ // (after the violating code ran) due to people who
+ // push/pop temporary policy in regions of code. So use
+ // the old policy here.
+ int policy = violation.getPolicy();
+
+ if ((policy & PENALTY_LOG) != 0) {
+ if (durationMillis != -1) {
+ Log.d(TAG, "StrictMode policy violation; ~duration=" + durationMillis + " ms",
+ violation);
+ } else {
+ Log.d(TAG, "StrictMode policy violation.", violation);
+ }
+ }
+
+ if ((policy & PENALTY_DIALOG) != 0) {
+ // Currently this is just used for the dialog.
+ try {
+ ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
+ RuntimeInit.getApplicationObject(),
+ new ApplicationErrorReport.CrashInfo(violation));
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException trying to open strict mode dialog", e);
+ }
+ }
+
+ if ((policy & PENALTY_DROPBOX) != 0) {
+ // TODO: call into ActivityManagerNative to do the dropboxing.
+ // But do the first-layer signature dup-checking first client-side.
+ // This conditional should be combined with the above, too, along
+ // with PENALTY_DEATH below.
+ }
+
+ if ((policy & PENALTY_DEATH) != 0) {
+ System.err.println("StrictMode policy violation with POLICY_DEATH; shutting down.");
+ Process.killProcess(Process.myPid());
+ System.exit(10);
+ }
+ }
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java
index 0a6415d..1da6d7a 100644
--- a/core/java/android/pim/vcard/VCardBuilder.java
+++ b/core/java/android/pim/vcard/VCardBuilder.java
@@ -642,22 +642,18 @@ public class VCardBuilder {
if (TextUtils.isEmpty(phoneNumber)) {
continue;
}
- int type = (typeAsObject != null ? typeAsObject : DEFAULT_PHONE_TYPE);
- if (type == Phone.TYPE_PAGER) {
+
+ // PAGER number needs unformatted "phone number".
+ final int type = (typeAsObject != null ? typeAsObject : DEFAULT_PHONE_TYPE);
+ if (type == Phone.TYPE_PAGER ||
+ VCardConfig.refrainPhoneNumberFormatting(mVCardType)) {
phoneLineExists = true;
if (!phoneSet.contains(phoneNumber)) {
phoneSet.add(phoneNumber);
appendTelLine(type, label, phoneNumber, isPrimary);
}
} else {
- // The entry "may" have several phone numbers when the contact entry is
- // corrupted because of its original source.
- //
- // e.g. I encountered the entry like the following.
- // "111-222-3333 (Miami)\n444-555-6666 (Broward; 305-653-6796 (Miami); ..."
- // This kind of entry is not able to be inserted via Android devices, but
- // possible if the source of the data is already corrupted.
- List<String> phoneNumberList = splitIfSeveralPhoneNumbersExist(phoneNumber);
+ final List<String> phoneNumberList = splitAndTrimPhoneNumbers(phoneNumber);
if (phoneNumberList.isEmpty()) {
continue;
}
@@ -670,7 +666,7 @@ public class VCardBuilder {
phoneSet.add(actualPhoneNumber);
appendTelLine(type, label, formattedPhoneNumber, isPrimary);
}
- }
+ } // for (String actualPhoneNumber : phoneNumberList) {
}
}
}
@@ -682,15 +678,38 @@ public class VCardBuilder {
return this;
}
- private List<String> splitIfSeveralPhoneNumbersExist(final String phoneNumber) {
- List<String> phoneList = new ArrayList<String>();
+ /**
+ * <p>
+ * Splits a given string expressing phone numbers into several strings, and remove
+ * unnecessary characters inside them. The size of a returned list becomes 1 when
+ * no split is needed.
+ * </p>
+ * <p>
+ * The given number "may" have several phone numbers when the contact entry is corrupted
+ * because of its original source.
+ * e.g. "111-222-3333 (Miami)\n444-555-6666 (Broward; 305-653-6796 (Miami)"
+ * </p>
+ * <p>
+ * This kind of "phone numbers" will not be created with Android vCard implementation,
+ * but we may encounter them if the source of the input data has already corrupted
+ * implementation.
+ * </p>
+ * <p>
+ * To handle this case, this method first splits its input into multiple parts
+ * (e.g. "111-222-3333 (Miami)", "444-555-6666 (Broward", and 305653-6796 (Miami)") and
+ * removes unnecessary strings like "(Miami)".
+ * </p>
+ * <p>
+ * Do not call this method when trimming is inappropriate for its receivers.
+ * </p>
+ */
+ private List<String> splitAndTrimPhoneNumbers(final String phoneNumber) {
+ final List<String> phoneList = new ArrayList<String>();
StringBuilder builder = new StringBuilder();
final int length = phoneNumber.length();
for (int i = 0; i < length; i++) {
final char ch = phoneNumber.charAt(i);
- // TODO: add a test case for string with '+', and care the other possible issues
- // which may happen by ignoring non-digits other than '+'.
if (Character.isDigit(ch) || ch == '+') {
builder.append(ch);
} else if ((ch == ';' || ch == '\n') && builder.length() > 0) {
diff --git a/core/java/android/pim/vcard/VCardConfig.java b/core/java/android/pim/vcard/VCardConfig.java
index 3409be6..8219840 100644
--- a/core/java/android/pim/vcard/VCardConfig.java
+++ b/core/java/android/pim/vcard/VCardConfig.java
@@ -15,6 +15,7 @@
*/
package android.pim.vcard;
+import android.telephony.PhoneNumberUtils;
import android.util.Log;
import java.util.HashMap;
@@ -190,6 +191,30 @@ public class VCardConfig {
*/
public static final int FLAG_REFRAIN_IMAGE_EXPORT = 0x02000000;
+ /**
+ * <P>
+ * The flag indicating the vCard composer does touch nothing toward phone number Strings
+ * but leave it as is.
+ * </P>
+ * <P>
+ * The vCard specifications mention nothing toward phone numbers, while some devices
+ * do (wrongly, but with innevitable reasons).
+ * For example, there's a possibility Japanese mobile phones are expected to have
+ * just numbers, hypens, plus, etc. but not usual alphabets, while US mobile phones
+ * should get such characters. To make exported vCard simple for external parsers,
+ * we have used {@link PhoneNumberUtils#formatNumber(String)} during export, and
+ * removed unnecessary characters inside the number (e.g. "111-222-3333 (Miami)"
+ * becomes "111-222-3333").
+ * Unfortunate side effect of that use was some control characters used in the other
+ * areas may be badly affected by the formatting.
+ * </P>
+ * <P>
+ * This flag disables that formatting, affecting both importer and exporter.
+ * If the user is aware of some side effects due to the implicit formatting, use this flag.
+ * </P>
+ */
+ public static final int FLAG_REFRAIN_PHONE_NUMBER_FORMATTING = 0x02000000;
+
//// The followings are VCard types available from importer/exporter. ////
/**
@@ -431,6 +456,10 @@ public class VCardConfig {
return sJapaneseMobileTypeSet.contains(vcardType);
}
+ /* package */ static boolean refrainPhoneNumberFormatting(final int vcardType) {
+ return ((vcardType & FLAG_REFRAIN_PHONE_NUMBER_FORMATTING) != 0);
+ }
+
public static boolean needsToConvertPhoneticString(final int vcardType) {
return ((vcardType & FLAG_CONVERT_PHONETIC_NAME_STRINGS) != 0);
}
@@ -445,4 +474,4 @@ public class VCardConfig {
private VCardConfig() {
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/pim/vcard/VCardEntry.java b/core/java/android/pim/vcard/VCardEntry.java
index 1327770..7c7e9b8 100644
--- a/core/java/android/pim/vcard/VCardEntry.java
+++ b/core/java/android/pim/vcard/VCardEntry.java
@@ -488,7 +488,7 @@ public class VCardEntry {
final StringBuilder builder = new StringBuilder();
final String trimed = data.trim();
final String formattedNumber;
- if (type == Phone.TYPE_PAGER) {
+ if (type == Phone.TYPE_PAGER || VCardConfig.refrainPhoneNumberFormatting(mVCardType)) {
formattedNumber = trimed;
} else {
final int length = trimed.length();
@@ -500,8 +500,7 @@ public class VCardEntry {
}
// Use NANP in default when there's no information about locale.
- final int formattingType = (VCardConfig.isJapaneseDevice(mVCardType) ?
- PhoneNumberUtils.FORMAT_JAPAN : PhoneNumberUtils.FORMAT_NANP);
+ final int formattingType = VCardUtils.getPhoneNumberFormat(mVCardType);
formattedNumber = PhoneNumberUtils.formatNumber(builder.toString(), formattingType);
}
PhoneData phoneData = new PhoneData(type, formattedNumber, label, isPrimary);
diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java
index cc48aeb..bbad2b6 100644
--- a/core/java/android/preference/DialogPreference.java
+++ b/core/java/android/preference/DialogPreference.java
@@ -33,7 +33,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
/**
@@ -275,7 +274,7 @@ public abstract class DialogPreference extends Preference implements
protected void showDialog(Bundle state) {
Context context = getContext();
- mWhichButtonClicked = DialogInterface.BUTTON2;
+ mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
mBuilder = new AlertDialog.Builder(context)
.setTitle(mDialogTitle)
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index c9d125b..40ed980 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1819,4 +1819,12 @@ public final class MediaStore {
* Name of current volume being scanned by the media scanner.
*/
public static final String MEDIA_SCANNER_VOLUME = "volume";
+
+ /**
+ * Name of the file signaling the media scanner to ignore media in the containing directory
+ * and its subdirectories. Developers should use this to avoid application graphics showing
+ * up in the Gallery and likewise prevent application sounds and music from showing up in
+ * the Music app.
+ */
+ public static final String MEDIA_IGNORE_FILENAME = ".nomedia";
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e12dfb0..7bb89f5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -355,6 +355,21 @@ public final class Settings {
"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
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 893db2e..a52a221 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -27,7 +27,6 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothA2dp;
-import android.os.ParcelUuid;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -35,6 +34,7 @@ import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
+import android.os.ParcelUuid;
import android.provider.Settings;
import android.util.Log;
@@ -55,8 +55,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private static final String BLUETOOTH_ENABLED = "bluetooth_enabled";
- private static final int MESSAGE_CONNECT_TO = 1;
-
private static final String PROPERTY_STATE = "State";
private static final String SINK_STATE_DISCONNECTED = "disconnected";
@@ -73,6 +71,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private final BluetoothService mBluetoothService;
private final BluetoothAdapter mAdapter;
private int mTargetA2dpState;
+ private boolean mAdjustedPriority = false;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -104,16 +103,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
setSinkPriority(device, BluetoothA2dp.PRIORITY_UNDEFINED);
break;
}
- } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
- if (getSinkPriority(device) == BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
- isSinkDevice(device)) {
- // This device is a preferred sink. Make an A2DP connection
- // after a delay. We delay to avoid connection collisions,
- // and to give other profiles such as HFP a chance to
- // connect first.
- Message msg = Message.obtain(mHandler, MESSAGE_CONNECT_TO, device);
- mHandler.sendMessageDelayed(msg, 6000);
- }
} else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
synchronized (this) {
if (mAudioDevices.containsKey(device)) {
@@ -187,6 +176,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
if (mBluetoothService.isEnabled())
onBluetoothEnable();
mTargetA2dpState = -1;
+ mBluetoothService.setA2dpService(this);
}
@Override
@@ -198,29 +188,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
}
}
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_CONNECT_TO:
- BluetoothDevice device = (BluetoothDevice) msg.obj;
- // check bluetooth is still on, device is still preferred, and
- // nothing is currently connected
- if (mBluetoothService.isEnabled() &&
- getSinkPriority(device) == BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
- lookupSinksMatchingStates(new int[] {
- BluetoothA2dp.STATE_CONNECTING,
- BluetoothA2dp.STATE_CONNECTED,
- BluetoothA2dp.STATE_PLAYING,
- BluetoothA2dp.STATE_DISCONNECTING}).size() == 0) {
- log("Auto-connecting A2DP to sink " + device);
- connectSink(device);
- }
- break;
- }
- }
- };
-
private int convertBluezSinkStringtoState(String value) {
if (value.equalsIgnoreCase("disconnected"))
return BluetoothA2dp.STATE_DISCONNECTED;
@@ -308,13 +275,37 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
mAudioManager.setParameters(BLUETOOTH_ENABLED + "=false");
}
+ private synchronized boolean isConnectSinkFeasible(BluetoothDevice device) {
+ if (!mBluetoothService.isEnabled() || !isSinkDevice(device) ||
+ getSinkPriority(device) == BluetoothA2dp.PRIORITY_OFF) {
+ return false;
+ }
+
+ if (mAudioDevices.get(device) == null && !addAudioSink(device)) {
+ return false;
+ }
+
+ String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+ if (path == null) {
+ return false;
+ }
+ return true;
+ }
+
public synchronized boolean connectSink(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
if (DBG) log("connectSink(" + device + ")");
+ if (!isConnectSinkFeasible(device)) return false;
+ return mBluetoothService.connectSink(device.getAddress());
+ }
+
+ public synchronized boolean connectSinkInternal(BluetoothDevice device) {
if (!mBluetoothService.isEnabled()) return false;
+ int state = mAudioDevices.get(device);
+
// ignore if there are any active sinks
if (lookupSinksMatchingStates(new int[] {
BluetoothA2dp.STATE_CONNECTING,
@@ -324,11 +315,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
return false;
}
- if (mAudioDevices.get(device) == null && !addAudioSink(device))
- return false;
-
- int state = mAudioDevices.get(device);
-
switch (state) {
case BluetoothA2dp.STATE_CONNECTED:
case BluetoothA2dp.STATE_PLAYING:
@@ -339,8 +325,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
}
String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
- if (path == null)
- return false;
// State is DISCONNECTED
handleSinkStateChange(device, state, BluetoothA2dp.STATE_CONNECTING);
@@ -353,11 +337,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
return true;
}
- public synchronized boolean disconnectSink(BluetoothDevice device) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- if (DBG) log("disconnectSink(" + device + ")");
-
+ private synchronized boolean isDisconnectSinkFeasible(BluetoothDevice device) {
String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
if (path == null) {
return false;
@@ -370,6 +350,20 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
case BluetoothA2dp.STATE_DISCONNECTING:
return true;
}
+ return true;
+ }
+
+ public synchronized boolean disconnectSink(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (DBG) log("disconnectSink(" + device + ")");
+ if (!isDisconnectSinkFeasible(device)) return false;
+ return mBluetoothService.disconnectSink(device.getAddress());
+ }
+
+ public synchronized boolean disconnectSinkInternal(BluetoothDevice device) {
+ int state = getSinkState(device);
+ String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
// State is CONNECTING or CONNECTED or PLAYING
handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTING);
@@ -504,6 +498,12 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
setSinkPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
}
+ if (state == BluetoothA2dp.STATE_CONNECTED) {
+ // We will only have 1 device with AUTO_CONNECT priority
+ // To be backward compatible set everyone else to have PRIORITY_ON
+ adjustOtherSinkPriorities(device);
+ }
+
Intent intent = new Intent(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.putExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, prevState);
@@ -514,6 +514,18 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
}
}
+ private void adjustOtherSinkPriorities(BluetoothDevice connectedDevice) {
+ if (!mAdjustedPriority) {
+ for (BluetoothDevice device : mAdapter.getBondedDevices()) {
+ if (getSinkPriority(device) >= BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
+ !device.equals(connectedDevice)) {
+ setSinkPriority(device, BluetoothA2dp.PRIORITY_ON);
+ }
+ }
+ mAdjustedPriority = true;
+ }
+ }
+
private synchronized Set<BluetoothDevice> lookupSinksMatchingStates(int[] states) {
Set<BluetoothDevice> sinks = new HashSet<BluetoothDevice>();
if (mAudioDevices.isEmpty()) {
@@ -554,6 +566,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
if (!result) {
if (deviceObjectPath != null) {
String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+ if (address == null) return;
BluetoothDevice device = mAdapter.getRemoteDevice(address);
int state = getSinkState(device);
handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED);
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index c0e4600..e1d3f13 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -566,6 +566,7 @@ class BluetoothEventLoop {
authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
if (authorized) {
Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
+ mBluetoothService.notifyIncomingA2dpConnection(address);
} else {
Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index c0affd3..31e5a7b 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -28,6 +28,8 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothDeviceProfileState;
+import android.bluetooth.BluetoothProfileState;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetooth;
@@ -112,7 +114,7 @@ public class BluetoothService extends IBluetooth.Stub {
BluetoothUuid.HSP,
BluetoothUuid.ObexObjectPush };
-
+ // TODO(): Optimize all these string handling
private final Map<String, String> mAdapterProperties;
private final HashMap<String, Map<String, String>> mDeviceProperties;
@@ -122,6 +124,11 @@ public class BluetoothService extends IBluetooth.Stub {
private final HashMap<Integer, Integer> mServiceRecordToPid;
+ private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
+ private final BluetoothProfileState mA2dpProfileState;
+ private final BluetoothProfileState mHfpProfileState;
+
+ private BluetoothA2dpService mA2dpService;
private static String mDockAddress;
private String mDockPin;
@@ -179,6 +186,12 @@ public class BluetoothService extends IBluetooth.Stub {
mUuidIntentTracker = new ArrayList<String>();
mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
mServiceRecordToPid = new HashMap<Integer, Integer>();
+ mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
+ mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
+ mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
+
+ mHfpProfileState.start();
+ mA2dpProfileState.start();
IntentFilter filter = new IntentFilter();
registerForAirplaneMode(filter);
@@ -187,7 +200,7 @@ public class BluetoothService extends IBluetooth.Stub {
mContext.registerReceiver(mReceiver, filter);
}
- public static synchronized String readDockBluetoothAddress() {
+ public static synchronized String readDockBluetoothAddress() {
if (mDockAddress != null) return mDockAddress;
BufferedInputStream file = null;
@@ -534,6 +547,7 @@ public class BluetoothService extends IBluetooth.Stub {
mIsDiscovering = false;
mBondState.readAutoPairingData();
mBondState.loadBondState();
+ initProfileState();
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 1, -1), 3000);
@@ -648,6 +662,12 @@ public class BluetoothService extends IBluetooth.Stub {
}
}
+ if (state == BluetoothDevice.BOND_BONDED) {
+ addProfileState(address);
+ } else if (state == BluetoothDevice.BOND_NONE) {
+ removeProfileState(address);
+ }
+
if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
reason + ")");
Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
@@ -1167,6 +1187,16 @@ public class BluetoothService extends IBluetooth.Stub {
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
return false;
}
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
+ if (state != null) {
+ state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public synchronized boolean removeBondInternal(String address) {
return removeDeviceNative(getObjectPathFromAddress(address));
}
@@ -1836,7 +1866,7 @@ public class BluetoothService extends IBluetooth.Stub {
// Rather not do this from here, but no-where else and I need this
// dump
pw.println("\n--Headset Service--");
- switch (headset.getState()) {
+ switch (headset.getState(headset.getCurrentHeadset())) {
case BluetoothHeadset.STATE_DISCONNECTED:
pw.println("getState() = STATE_DISCONNECTED");
break;
@@ -1919,6 +1949,116 @@ public class BluetoothService extends IBluetooth.Stub {
if (!result) log("Set Link Timeout to:" + num_slots + " slots failed");
}
+ public boolean connectHeadset(String address) {
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
+ if (state != null) {
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
+ msg.obj = state;
+ mHfpProfileState.sendMessage(msg);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean disconnectHeadset(String address) {
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
+ if (state != null) {
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
+ msg.obj = state;
+ mHfpProfileState.sendMessage(msg);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean connectSink(String address) {
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
+ if (state != null) {
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
+ msg.obj = state;
+ mA2dpProfileState.sendMessage(msg);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean disconnectSink(String address) {
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
+ if (state != null) {
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
+ msg.obj = state;
+ mA2dpProfileState.sendMessage(msg);
+ return true;
+ }
+ return false;
+ }
+
+ private BluetoothDeviceProfileState addProfileState(String address) {
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
+ if (state != null) return state;
+
+ state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService);
+ mDeviceProfileState.put(address, state);
+ state.start();
+ return state;
+ }
+
+ private void removeProfileState(String address) {
+ mDeviceProfileState.remove(address);
+ }
+
+ private void initProfileState() {
+ String []bonds = null;
+ String val = getPropertyInternal("Devices");
+ if (val != null) {
+ bonds = val.split(",");
+ }
+ if (bonds == null) {
+ return;
+ }
+
+ for (String path : bonds) {
+ String address = getAddressFromObjectPath(path);
+ BluetoothDeviceProfileState state = addProfileState(address);
+ // Allow 8 secs for SDP records to get registered.
+ Message msg = new Message();
+ msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
+ state.sendMessageDelayed(msg, 8000);
+ }
+ }
+
+ public boolean notifyIncomingConnection(String address) {
+ BluetoothDeviceProfileState state =
+ mDeviceProfileState.get(address);
+ if (state != null) {
+ Message msg = new Message();
+ msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
+ state.sendMessage(msg);
+ return true;
+ }
+ return false;
+ }
+
+ /*package*/ boolean notifyIncomingA2dpConnection(String address) {
+ BluetoothDeviceProfileState state =
+ mDeviceProfileState.get(address);
+ if (state != null) {
+ Message msg = new Message();
+ msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
+ state.sendMessage(msg);
+ return true;
+ }
+ return false;
+ }
+
+ /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
+ mA2dpService = a2dpService;
+ }
+
private static void log(String msg) {
Log.d(TAG, msg);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 3d1d7d6..2ade44e 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -522,20 +522,14 @@ public abstract class WallpaperService extends Service {
}
try {
- SurfaceHolder.Callback callbacks[] = null;
- synchronized (mSurfaceHolder.mCallbacks) {
- final int N = mSurfaceHolder.mCallbacks.size();
- if (N > 0) {
- callbacks = new SurfaceHolder.Callback[N];
- mSurfaceHolder.mCallbacks.toArray(callbacks);
- }
- }
+ mSurfaceHolder.ungetCallbacks();
if (surfaceCreating) {
mIsCreating = true;
if (DEBUG) Log.v(TAG, "onSurfaceCreated("
+ mSurfaceHolder + "): " + this);
onSurfaceCreated(mSurfaceHolder);
+ SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
@@ -557,6 +551,7 @@ public abstract class WallpaperService extends Service {
+ "): " + this);
onSurfaceChanged(mSurfaceHolder, mFormat,
mCurWidth, mCurHeight);
+ SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceChanged(mSurfaceHolder, mFormat,
@@ -698,14 +693,12 @@ public abstract class WallpaperService extends Service {
void reportSurfaceDestroyed() {
if (mSurfaceCreated) {
mSurfaceCreated = false;
- SurfaceHolder.Callback callbacks[];
- synchronized (mSurfaceHolder.mCallbacks) {
- callbacks = new SurfaceHolder.Callback[
- mSurfaceHolder.mCallbacks.size()];
- mSurfaceHolder.mCallbacks.toArray(callbacks);
- }
- for (SurfaceHolder.Callback c : callbacks) {
- c.surfaceDestroyed(mSurfaceHolder);
+ mSurfaceHolder.ungetCallbacks();
+ SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
+ if (callbacks != null) {
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceDestroyed(mSurfaceHolder);
+ }
}
if (DEBUG) Log.v(TAG, "onSurfaceDestroyed("
+ mSurfaceHolder + "): " + this);
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 @@ public class Rfc822Tokenizer implements MultiAutoCompleteTextView.Tokenizer {
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 @@ public class Rfc822Tokenizer implements MultiAutoCompleteTextView.Tokenizer {
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/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 2628eb4..76d8106 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -135,6 +135,7 @@ public class DisplayMetrics {
int screenLayout) {
boolean expandable = compatibilityInfo.isConfiguredExpandable();
boolean largeScreens = compatibilityInfo.isConfiguredLargeScreens();
+ boolean xlargeScreens = compatibilityInfo.isConfiguredXLargeScreens();
// Note: this assume that configuration is updated before calling
// updateMetrics method.
@@ -157,8 +158,18 @@ public class DisplayMetrics {
compatibilityInfo.setLargeScreens(false);
}
}
+ if (!xlargeScreens) {
+ if ((screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK)
+ != Configuration.SCREENLAYOUT_SIZE_XLARGE) {
+ xlargeScreens = true;
+ // the current screen size is not large.
+ compatibilityInfo.setXLargeScreens(true);
+ } else {
+ compatibilityInfo.setXLargeScreens(false);
+ }
+ }
- if (!expandable || !largeScreens) {
+ if (!expandable || (!largeScreens && !xlargeScreens)) {
// This is a larger screen device and the app is not
// compatible with large screens, so diddle it.
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index e111669..d577b74 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -88,6 +88,21 @@ public final class Log {
TerribleFailure(String msg, Throwable cause) { super(msg, cause); }
}
+ /**
+ * Interface to handle terrible failures from {@link #wtf()}.
+ *
+ * @hide
+ */
+ public interface TerribleFailureHandler {
+ void onTerribleFailure(String tag, TerribleFailure what);
+ }
+
+ private static TerribleFailureHandler sWtfHandler = new TerribleFailureHandler() {
+ public void onTerribleFailure(String tag, TerribleFailure what) {
+ RuntimeInit.wtf(tag, what);
+ }
+ };
+
private Log() {
}
@@ -257,13 +272,29 @@ public final class Log {
* @param tr An exception to log. May be null.
*/
public static int wtf(String tag, String msg, Throwable tr) {
- tr = new TerribleFailure(msg, tr);
+ TerribleFailure what = new TerribleFailure(msg, tr);
int bytes = println_native(LOG_ID_MAIN, ASSERT, tag, getStackTraceString(tr));
- RuntimeInit.wtf(tag, tr);
+ sWtfHandler.onTerribleFailure(tag, what);
return bytes;
}
/**
+ * Sets the terrible failure handler, for testing.
+ *
+ * @return the old handler
+ *
+ * @hide
+ */
+ public static TerribleFailureHandler setWtfHandler(TerribleFailureHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException("handler == null");
+ }
+ TerribleFailureHandler oldHandler = sWtfHandler;
+ sWtfHandler = handler;
+ return oldHandler;
+ }
+
+ /**
* Handy function to get a loggable stack trace from a Throwable
* @param tr An exception to log
*/
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index d4f9787..9aa16b5 100644..100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -120,6 +120,10 @@ public class KeyEvent implements Parcelable {
public static final int KEYCODE_MEDIA_REWIND = 89;
public static final int KEYCODE_MEDIA_FAST_FORWARD = 90;
public static final int KEYCODE_MUTE = 91;
+ public static final int KEYCODE_PAGE_UP = 92;
+ public static final int KEYCODE_PAGE_DOWN = 93;
+ public static final int KEYCODE_PICTSYMBOLS = 94; // switch symbol-sets (Emoji,Kao-moji)
+ public static final int KEYCODE_SWITCH_CHARSET = 95; // switch char-sets (Kanji,Katakana)
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
@@ -135,7 +139,7 @@ public class KeyEvent implements Parcelable {
// those new codes. This is intended to maintain a consistent
// set of key code definitions across all Android devices.
- private static final int LAST_KEYCODE = KEYCODE_MUTE;
+ private static final int LAST_KEYCODE = KEYCODE_SWITCH_CHARSET;
/**
* @deprecated There are now more than MAX_KEYCODE keycodes.
@@ -692,6 +696,8 @@ public class KeyEvent implements Parcelable {
case KEYCODE_CAMERA:
case KEYCODE_FOCUS:
case KEYCODE_SEARCH:
+ case KEYCODE_PICTSYMBOLS:
+ case KEYCODE_SWITCH_CHARSET:
return true;
default:
return false;
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 @@ public final class MotionEvent implements Parcelable {
}
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 @@ public final class MotionEvent implements Parcelable {
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/view/Surface.java b/core/java/android/view/Surface.java
index 83ef8ba..cd0ae3b 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -140,13 +140,13 @@ public class Surface implements Parcelable {
public static final int FLAGS_ORIENTATION_ANIMATION_DISABLE = 0x000000001;
@SuppressWarnings("unused")
- private int mSurface;
- @SuppressWarnings("unused")
private int mSurfaceControl;
@SuppressWarnings("unused")
private int mSaveCount;
@SuppressWarnings("unused")
private Canvas mCanvas;
+ @SuppressWarnings("unused")
+ private int mNativeSurface;
private String mName;
// The display metrics used to provide the pseudo canvas size for applications
@@ -422,13 +422,13 @@ public class Surface implements Parcelable {
/* no user serviceable parts here ... */
@Override
protected void finalize() throws Throwable {
- if (mSurface != 0 || mSurfaceControl != 0) {
+ if (mNativeSurface != 0 || mSurfaceControl != 0) {
if (DEBUG_RELEASE) {
Log.w(LOG_TAG, "Surface.finalize() has work. You should have called release() ("
- + mSurface + ", " + mSurfaceControl + ")", mCreationStack);
+ + mNativeSurface + ", " + mSurfaceControl + ")", mCreationStack);
} else {
Log.w(LOG_TAG, "Surface.finalize() has work. You should have called release() ("
- + mSurface + ", " + mSurfaceControl + ")");
+ + mNativeSurface + ", " + mSurfaceControl + ")");
}
}
release();
diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java
index 64a10d1..34e4638 100644
--- a/core/java/android/view/SurfaceHolder.java
+++ b/core/java/android/view/SurfaceHolder.java
@@ -182,7 +182,6 @@ public interface SurfaceHolder {
/**
* Enable or disable option to keep the screen turned on while this
* surface is displayed. The default is false, allowing it to turn off.
- * Enabling the option effectivelty.
* This is safe to call from any thread.
*
* @param screenOn Supply to true to force the screen to stay on, false
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 53f0c2e..0f0cf60 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -141,7 +141,10 @@ public class SurfaceView extends View {
boolean mViewVisibility = false;
int mRequestedWidth = -1;
int mRequestedHeight = -1;
- int mRequestedFormat = PixelFormat.OPAQUE;
+ /* Set SurfaceView's format to 565 by default to maintain backward
+ * compatibility with applications assuming this format.
+ */
+ int mRequestedFormat = PixelFormat.RGB_565;
int mRequestedType = -1;
boolean mHaveFrame = false;
@@ -164,16 +167,20 @@ public class SurfaceView extends View {
public SurfaceView(Context context) {
super(context);
- setWillNotDraw(true);
+ init();
}
public SurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
- setWillNotDraw(true);
+ init();
}
public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ init();
+ }
+
+ private void init() {
setWillNotDraw(true);
}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index aab76c4..e69b807 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -206,7 +206,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
final long oldestTime = pastTime[oldestTouch];
float accumX = 0;
float accumY = 0;
- float N = (lastTouch - oldestTouch + NUM_PAST) % NUM_PAST + 1;
+ int N = (lastTouch - oldestTouch + NUM_PAST) % NUM_PAST + 1;
// Skip the last received event, since it is probably pretty noisy.
if (N > 3) N--;
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 03efea9..aa124e6 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -16,8 +16,10 @@
package android.view;
+import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.IInputMethodCallback;
import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.RootViewSurfaceTaker;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
@@ -26,12 +28,12 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.os.*;
import android.os.Process;
-import android.os.SystemProperties;
import android.util.AndroidRuntimeException;
import android.util.Config;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.EventLog;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityEvent;
@@ -50,6 +52,7 @@ import android.Manifest;
import android.media.AudioManager;
import java.lang.ref.WeakReference;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
@@ -76,6 +79,7 @@ public final class ViewRoot extends Handler implements ViewParent,
/** @noinspection PointlessBooleanExpression*/
private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;
private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV;
+ private static final boolean DEBUG_INPUT = true || LOCAL_LOGV;
private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV;
private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV;
private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;
@@ -133,6 +137,11 @@ public final class ViewRoot extends Handler implements ViewParent,
int mViewVisibility;
boolean mAppVisible = true;
+ SurfaceHolder.Callback mSurfaceHolderCallback;
+ BaseSurfaceHolder mSurfaceHolder;
+ boolean mIsCreating;
+ boolean mDrawingAllowed;
+
final Region mTransparentRegion;
final Region mPreviousTransparentRegion;
@@ -425,6 +434,9 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
+ // fd [0] is the receiver, [1] is the sender
+ private native int[] makeInputChannel();
+
/**
* We have one child
*/
@@ -435,6 +447,13 @@ public final class ViewRoot extends Handler implements ViewParent,
mView = view;
mWindowAttributes.copyFrom(attrs);
attrs = mWindowAttributes;
+ if (view instanceof RootViewSurfaceTaker) {
+ mSurfaceHolderCallback =
+ ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
+ if (mSurfaceHolderCallback != null) {
+ mSurfaceHolder = new TakenSurfaceHolder();
+ }
+ }
Resources resources = mView.getContext().getResources();
CompatibilityInfo compatibilityInfo = resources.getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
@@ -469,6 +488,14 @@ public final class ViewRoot extends Handler implements ViewParent,
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
+ // Set up the input event channel
+ if (false) {
+ int[] fds = makeInputChannel();
+ if (DEBUG_INPUT) {
+ Log.v(TAG, "makeInputChannel() returned " + fds);
+ }
+ }
+
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
@@ -682,6 +709,7 @@ public final class ViewRoot extends Handler implements ViewParent,
boolean windowResizesToFitContent = false;
boolean fullRedrawNeeded = mFullRedrawNeeded;
boolean newSurface = false;
+ boolean surfaceChanged = false;
WindowManager.LayoutParams lp = mWindowAttributes;
int desiredWindowWidth;
@@ -700,6 +728,7 @@ public final class ViewRoot extends Handler implements ViewParent,
WindowManager.LayoutParams params = null;
if (mWindowAttributesChanged) {
mWindowAttributesChanged = false;
+ surfaceChanged = true;
params = lp;
}
Rect frame = mWinFrame;
@@ -886,11 +915,18 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
+ if (mSurfaceHolder != null) {
+ mSurfaceHolder.mSurfaceLock.lock();
+ mDrawingAllowed = true;
+ lp.format = mSurfaceHolder.getRequestedFormat();
+ lp.type = mSurfaceHolder.getRequestedType();
+ }
+
boolean initialized = false;
boolean contentInsetsChanged = false;
boolean visibleInsetsChanged;
+ boolean hadSurface = mSurface.isValid();
try {
- boolean hadSurface = mSurface.isValid();
int fl = 0;
if (params != null) {
fl = params.flags;
@@ -965,6 +1001,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
} catch (RemoteException e) {
}
+
if (DEBUG_ORIENTATION) Log.v(
"ViewRoot", "Relayout returned: frame=" + frame + ", surface=" + mSurface);
@@ -977,6 +1014,57 @@ public final class ViewRoot extends Handler implements ViewParent,
mWidth = frame.width();
mHeight = frame.height();
+ if (mSurfaceHolder != null) {
+ // The app owns the surface; tell it about what is going on.
+ if (mSurface.isValid()) {
+ // XXX .copyFrom() doesn't work!
+ //mSurfaceHolder.mSurface.copyFrom(mSurface);
+ mSurfaceHolder.mSurface = mSurface;
+ }
+ mSurfaceHolder.mSurfaceLock.unlock();
+ if (mSurface.isValid()) {
+ if (!hadSurface) {
+ mSurfaceHolder.ungetCallbacks();
+
+ mIsCreating = true;
+ mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder);
+ SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
+ if (callbacks != null) {
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceCreated(mSurfaceHolder);
+ }
+ }
+ surfaceChanged = true;
+ }
+ if (surfaceChanged) {
+ mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,
+ lp.format, mWidth, mHeight);
+ SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
+ if (callbacks != null) {
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceChanged(mSurfaceHolder, lp.format,
+ mWidth, mHeight);
+ }
+ }
+ }
+ mIsCreating = false;
+ } else if (hadSurface) {
+ mSurfaceHolder.ungetCallbacks();
+ SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
+ mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder);
+ if (callbacks != null) {
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceDestroyed(mSurfaceHolder);
+ }
+ }
+ mSurfaceHolder.mSurfaceLock.lock();
+ // Make surface invalid.
+ //mSurfaceHolder.mSurface.copyFrom(mSurface);
+ mSurfaceHolder.mSurface = new Surface();
+ mSurfaceHolder.mSurfaceLock.unlock();
+ }
+ }
+
if (initialized) {
mGlCanvas.setViewport((int) (mWidth * appScale + 0.5f),
(int) (mHeight * appScale + 0.5f));
@@ -1268,6 +1356,12 @@ public final class ViewRoot extends Handler implements ViewParent,
boolean scalingRequired = mAttachInfo.mScalingRequired;
Rect dirty = mDirty;
+ if (mSurfaceHolder != null) {
+ // The app owns the surface, we won't draw.
+ dirty.setEmpty();
+ return;
+ }
+
if (mUseGL) {
if (!dirty.isEmpty()) {
Canvas canvas = mGlCanvas;
@@ -1332,103 +1426,105 @@ public final class ViewRoot extends Handler implements ViewParent,
appScale + ", width=" + mWidth + ", height=" + mHeight);
}
- Canvas canvas;
- try {
- int left = dirty.left;
- int top = dirty.top;
- int right = dirty.right;
- int bottom = dirty.bottom;
- canvas = surface.lockCanvas(dirty);
-
- if (left != dirty.left || top != dirty.top || right != dirty.right ||
- bottom != dirty.bottom) {
- mAttachInfo.mIgnoreDirtyState = true;
- }
-
- // TODO: Do this in native
- canvas.setDensity(mDensity);
- } catch (Surface.OutOfResourcesException e) {
- Log.e("ViewRoot", "OutOfResourcesException locking surface", e);
- // TODO: we should ask the window manager to do something!
- // for now we just do nothing
- return;
- } catch (IllegalArgumentException e) {
- Log.e("ViewRoot", "IllegalArgumentException locking surface", e);
- // TODO: we should ask the window manager to do something!
- // for now we just do nothing
- return;
- }
+ if (!dirty.isEmpty() || mIsAnimating) {
+ Canvas canvas;
+ try {
+ int left = dirty.left;
+ int top = dirty.top;
+ int right = dirty.right;
+ int bottom = dirty.bottom;
+ canvas = surface.lockCanvas(dirty);
+
+ if (left != dirty.left || top != dirty.top || right != dirty.right ||
+ bottom != dirty.bottom) {
+ mAttachInfo.mIgnoreDirtyState = true;
+ }
- try {
- if (!dirty.isEmpty() || mIsAnimating) {
- long startTime = 0L;
+ // TODO: Do this in native
+ canvas.setDensity(mDensity);
+ } catch (Surface.OutOfResourcesException e) {
+ Log.e("ViewRoot", "OutOfResourcesException locking surface", e);
+ // TODO: we should ask the window manager to do something!
+ // for now we just do nothing
+ return;
+ } catch (IllegalArgumentException e) {
+ Log.e("ViewRoot", "IllegalArgumentException locking surface", e);
+ // TODO: we should ask the window manager to do something!
+ // for now we just do nothing
+ return;
+ }
- if (DEBUG_ORIENTATION || DEBUG_DRAW) {
- Log.v("ViewRoot", "Surface " + surface + " drawing to bitmap w="
- + canvas.getWidth() + ", h=" + canvas.getHeight());
- //canvas.drawARGB(255, 255, 0, 0);
- }
+ try {
+ if (!dirty.isEmpty() || mIsAnimating) {
+ long startTime = 0L;
- if (Config.DEBUG && ViewDebug.profileDrawing) {
- startTime = SystemClock.elapsedRealtime();
- }
+ if (DEBUG_ORIENTATION || DEBUG_DRAW) {
+ Log.v("ViewRoot", "Surface " + surface + " drawing to bitmap w="
+ + canvas.getWidth() + ", h=" + canvas.getHeight());
+ //canvas.drawARGB(255, 255, 0, 0);
+ }
- // If this bitmap's format includes an alpha channel, we
- // need to clear it before drawing so that the child will
- // properly re-composite its drawing on a transparent
- // background. This automatically respects the clip/dirty region
- // or
- // If we are applying an offset, we need to clear the area
- // where the offset doesn't appear to avoid having garbage
- // left in the blank areas.
- if (!canvas.isOpaque() || yoff != 0) {
- canvas.drawColor(0, PorterDuff.Mode.CLEAR);
- }
+ if (Config.DEBUG && ViewDebug.profileDrawing) {
+ startTime = SystemClock.elapsedRealtime();
+ }
- dirty.setEmpty();
- mIsAnimating = false;
- mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
- mView.mPrivateFlags |= View.DRAWN;
+ // If this bitmap's format includes an alpha channel, we
+ // need to clear it before drawing so that the child will
+ // properly re-composite its drawing on a transparent
+ // background. This automatically respects the clip/dirty region
+ // or
+ // If we are applying an offset, we need to clear the area
+ // where the offset doesn't appear to avoid having garbage
+ // left in the blank areas.
+ if (!canvas.isOpaque() || yoff != 0) {
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ }
- if (DEBUG_DRAW) {
- Context cxt = mView.getContext();
- Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
- ", metrics=" + cxt.getResources().getDisplayMetrics() +
- ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
- }
- int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
- try {
- canvas.translate(0, -yoff);
- if (mTranslator != null) {
- mTranslator.translateCanvas(canvas);
+ dirty.setEmpty();
+ mIsAnimating = false;
+ mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
+ mView.mPrivateFlags |= View.DRAWN;
+
+ if (DEBUG_DRAW) {
+ Context cxt = mView.getContext();
+ Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
+ ", metrics=" + cxt.getResources().getDisplayMetrics() +
+ ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
+ }
+ int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ try {
+ canvas.translate(0, -yoff);
+ if (mTranslator != null) {
+ mTranslator.translateCanvas(canvas);
+ }
+ canvas.setScreenDensity(scalingRequired
+ ? DisplayMetrics.DENSITY_DEVICE : 0);
+ mView.draw(canvas);
+ } finally {
+ mAttachInfo.mIgnoreDirtyState = false;
+ canvas.restoreToCount(saveCount);
}
- canvas.setScreenDensity(scalingRequired
- ? DisplayMetrics.DENSITY_DEVICE : 0);
- mView.draw(canvas);
- } finally {
- mAttachInfo.mIgnoreDirtyState = false;
- canvas.restoreToCount(saveCount);
- }
- if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
- mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
- }
+ if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
+ mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
+ }
- if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
- int now = (int)SystemClock.elapsedRealtime();
- if (sDrawTime != 0) {
- nativeShowFPS(canvas, now - sDrawTime);
+ if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
+ int now = (int)SystemClock.elapsedRealtime();
+ if (sDrawTime != 0) {
+ nativeShowFPS(canvas, now - sDrawTime);
+ }
+ sDrawTime = now;
}
- sDrawTime = now;
- }
- if (Config.DEBUG && ViewDebug.profileDrawing) {
- EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
+ if (Config.DEBUG && ViewDebug.profileDrawing) {
+ EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
+ }
}
- }
- } finally {
- surface.unlockCanvasAndPost(canvas);
+ } finally {
+ surface.unlockCanvasAndPost(canvas);
+ }
}
if (LOCAL_LOGV) {
@@ -2813,6 +2909,46 @@ public final class ViewRoot extends Handler implements ViewParent,
return scrollToRectOrFocus(rectangle, immediate);
}
+ class TakenSurfaceHolder extends BaseSurfaceHolder {
+ @Override
+ public boolean onAllowLockCanvas() {
+ return mDrawingAllowed;
+ }
+
+ @Override
+ public void onRelayoutContainer() {
+ // Not currently interesting -- from changing between fixed and layout size.
+ }
+
+ public void setFormat(int format) {
+ ((RootViewSurfaceTaker)mView).setSurfaceFormat(format);
+ }
+
+ public void setType(int type) {
+ ((RootViewSurfaceTaker)mView).setSurfaceType(type);
+ }
+
+ @Override
+ public void onUpdateSurface() {
+ // We take care of format and type changes on our own.
+ throw new IllegalStateException("Shouldn't be here");
+ }
+
+ public boolean isCreating() {
+ return mIsCreating;
+ }
+
+ @Override
+ public void setFixedSize(int width, int height) {
+ throw new UnsupportedOperationException(
+ "Currently only support sizing from layout");
+ }
+
+ public void setKeepScreenOn(boolean screenOn) {
+ ((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn);
+ }
+ }
+
static class InputMethodCallback extends IInputMethodCallback.Stub {
private WeakReference<ViewRoot> mViewRoot;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 7dd5085..234deba 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -473,6 +473,14 @@ public abstract class Window {
}
/**
+ * Take ownership of this window's surface. The window's view hierarchy
+ * will no longer draw into the surface, though it will otherwise continue
+ * to operate (such as for receiving input events). The given SurfaceHolder
+ * callback will be used to tell you about state changes to the surface.
+ */
+ public abstract void takeSurface(SurfaceHolder.Callback callback);
+
+ /**
* Return whether this window is being displayed with a floating style
* (based on the {@link android.R.attr#windowIsFloating} attribute in
* the style/theme).
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index 034c88a..ca9ad53 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -67,7 +67,7 @@ public class MimeTypeMap {
// if the filename contains special characters, we don't
// consider it valid for our matching purposes:
if (filename.length() > 0 &&
- Pattern.matches("[a-zA-Z_0-9\\.\\-\\(\\)]+", filename)) {
+ Pattern.matches("[a-zA-Z_0-9\\.\\-\\(\\)\\%]+", filename)) {
int dotPos = filename.lastIndexOf('.');
if (0 <= dotPos) {
return filename.substring(dotPos + 1);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 921d0f5..bf751f5 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -238,7 +238,7 @@ import junit.framework.Assert;
*
* <p>The screen density of a device is based on the screen resolution. A screen with low density
* has fewer available pixels per inch, where a screen with high density
- * has more — sometimes significantly more — pixels per inch. The density of a
+ * has more &mdash; sometimes significantly more &mdash; pixels per inch. The density of a
* screen is important because, other things being equal, a UI element (such as a button) whose
* height and width are defined in terms of screen pixels will appear larger on the lower density
* screen and smaller on the higher density screen.
@@ -1380,16 +1380,23 @@ public class WebView extends AbsoluteLayout
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 @@ public class WebView extends AbsoluteLayout
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/DatePicker.java b/core/java/android/widget/DatePicker.java
index b3d5f1a..1fc23ab 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -328,6 +328,7 @@ public class DatePicker extends FrameLayout {
mYear = ss.getYear();
mMonth = ss.getMonth();
mDay = ss.getDay();
+ updateSpinners();
}
/**
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index f34823c..1ed6b16 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -1087,7 +1087,7 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// Gallery steals all key events
- return event.dispatch(this);
+ return event.dispatch(this, null, null);
}
/**
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index c246c247..39b1377 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -123,7 +123,7 @@ public class MediaController extends FrameLayout {
}
private void initFloatingWindow() {
- mWindowManager = (WindowManager)mContext.getSystemService("window");
+ mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mWindow = PolicyManager.makeNewWindow(mContext);
mWindow.setWindowManager(mWindowManager, null, null);
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 202e658..8e9eb05 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -947,4 +947,20 @@ public class ProgressBar extends View {
setProgress(ss.progress);
setSecondaryProgress(ss.secondaryProgress);
}
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mIndeterminate) {
+ startAnimation();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mIndeterminate) {
+ stopAnimation();
+ }
+ }
}
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 @@ public class RemoteViews implements Parcelable, Filter {
* 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 class RemoteViews implements Parcelable, Filter {
}
}
+ 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/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 64c9c99..950012c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4547,6 +4547,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
outText.text = TextUtils.substring(content, partialStartOffset,
partialEndOffset);
}
+ } else {
+ outText.partialStartOffset = 0;
+ outText.partialEndOffset = 0;
+ outText.text = "";
}
outText.flags = 0;
if (MetaKeyKeyListener.getMetaState(mText, MetaKeyKeyListener.META_SELECTING) != 0) {
diff --git a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
index 7e9bbd1..98dcb8b 100644
--- a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
+++ b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
@@ -23,13 +23,10 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
-import android.os.Handler;
import android.os.storage.IMountService;
-import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Environment;
-import android.widget.Toast;
import android.util.Log;
/**
@@ -38,7 +35,7 @@ import android.util.Log;
*/
public class ExternalMediaFormatActivity extends AlertActivity implements DialogInterface.OnClickListener {
- private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1;
+ private static final int POSITIVE_BUTTON = AlertDialog.BUTTON_POSITIVE;
/** Used to detect when the media state changes, in case we need to call finish() */
private BroadcastReceiver mStorageReceiver = new BroadcastReceiver() {
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/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 24818a8..36f45b2 100755
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -23,14 +23,9 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.widget.Toast;
import android.util.Log;
import android.location.LocationManager;
-import com.android.internal.location.GpsLocationProvider;
import com.android.internal.location.GpsNetInitiatedHandler;
/**
@@ -44,8 +39,8 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa
private static final boolean DEBUG = true;
private static final boolean VERBOSE = false;
- private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1;
- private static final int NEGATIVE_BUTTON = AlertDialog.BUTTON2;
+ private static final int POSITIVE_BUTTON = AlertDialog.BUTTON_POSITIVE;
+ private static final int NEGATIVE_BUTTON = AlertDialog.BUTTON_NEGATIVE;
// Dialog button text
public static final String BUTTON_TEXT_ACCEPT = "Accept";
diff --git a/core/java/com/android/internal/app/RingtonePickerActivity.java b/core/java/com/android/internal/app/RingtonePickerActivity.java
index ddddabe..5569ffe 100644
--- a/core/java/com/android/internal/app/RingtonePickerActivity.java
+++ b/core/java/com/android/internal/app/RingtonePickerActivity.java
@@ -223,7 +223,7 @@ public final class RingtonePickerActivity extends AlertActivity implements
* On click of Ok/Cancel buttons
*/
public void onClick(DialogInterface dialog, int which) {
- boolean positiveResult = which == BUTTON1;
+ boolean positiveResult = which == DialogInterface.BUTTON_POSITIVE;
// Stop playing the previous ringtone
mRingtoneManager.stopPreviousRingtone();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index aadb576..3833725 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -56,12 +56,13 @@ import java.util.concurrent.atomic.AtomicInteger;
public final class BatteryStatsImpl extends BatteryStats {
private static final String TAG = "BatteryStatsImpl";
private static final boolean DEBUG = false;
-
+ private static final boolean DEBUG_HISTORY = false;
+
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 43;
+ private static final int VERSION = 44;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -94,6 +95,11 @@ public final class BatteryStatsImpl extends BatteryStats {
// is unplugged from power.
final ArrayList<Unpluggable> mUnpluggables = new ArrayList<Unpluggable>();
+ BatteryHistoryRecord mHistory;
+ BatteryHistoryRecord mHistoryEnd;
+ BatteryHistoryRecord mHistoryCache;
+ final BatteryHistoryRecord mHistoryCur = new BatteryHistoryRecord();
+
int mStartCount;
long mBatteryUptime;
@@ -1042,6 +1048,37 @@ public final class BatteryStatsImpl extends BatteryStats {
mBtHeadset = headset;
}
+ void addHistoryRecord(long curTime) {
+ BatteryHistoryRecord rec = mHistoryCache;
+ if (rec != null) {
+ mHistoryCache = rec.next;
+ } else {
+ rec = new BatteryHistoryRecord();
+ }
+ rec.time = curTime;
+ rec.batteryLevel = mHistoryCur.batteryLevel;
+ rec.states = mHistoryCur.states;
+ addHistoryRecord(rec);
+ }
+
+ void addHistoryRecord(BatteryHistoryRecord rec) {
+ rec.next = null;
+ if (mHistoryEnd != null) {
+ mHistoryEnd.next = rec;
+ mHistoryEnd = rec;
+ } else {
+ mHistory = mHistoryEnd = rec;
+ }
+ }
+
+ void clearHistory() {
+ if (mHistory != null) {
+ mHistoryEnd.next = mHistoryCache;
+ mHistoryCache = mHistory;
+ mHistory = mHistoryEnd = null;
+ }
+ }
+
public void doUnplug(long batteryUptime, long batteryRealtime) {
for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
Uid u = mUidStats.valueAt(iu);
@@ -1094,16 +1131,37 @@ public final class BatteryStatsImpl extends BatteryStats {
mBluetoothPingStart = -1;
}
+ int mGpsNesting;
+
public void noteStartGps(int uid) {
+ if (mGpsNesting == 0) {
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_GPS_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
+ }
+ mGpsNesting++;
getUidStatsLocked(uid).noteStartGps();
}
public void noteStopGps(int uid) {
+ mGpsNesting--;
+ if (mGpsNesting == 0) {
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_GPS_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
+ }
getUidStatsLocked(uid).noteStopGps();
}
public void noteScreenOnLocked() {
if (!mScreenOn) {
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_SCREEN_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
+ mGpsNesting++;
mScreenOn = true;
mScreenOnTimer.startRunningLocked(this);
if (mScreenBrightnessBin >= 0) {
@@ -1114,6 +1172,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteScreenOffLocked() {
if (mScreenOn) {
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_SCREEN_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mScreenOn = false;
mScreenOnTimer.stopRunningLocked(this);
if (mScreenBrightnessBin >= 0) {
@@ -1128,6 +1190,11 @@ public final class BatteryStatsImpl extends BatteryStats {
if (bin < 0) bin = 0;
else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1;
if (mScreenBrightnessBin != bin) {
+ mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_SCREEN_MASK)
+ | (bin << BatteryHistoryRecord.STATE_SCREEN_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Screen brightness " + bin + " to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
if (mScreenOn) {
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
@@ -1148,6 +1215,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void notePhoneOnLocked() {
if (!mPhoneOn) {
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_PHONE_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mPhoneOn = true;
mPhoneOnTimer.startRunningLocked(this);
}
@@ -1155,6 +1226,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void notePhoneOffLocked() {
if (mPhoneOn) {
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_PHONE_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mPhoneOn = false;
mPhoneOnTimer.stopRunningLocked(this);
}
@@ -1195,7 +1270,15 @@ public final class BatteryStatsImpl extends BatteryStats {
mPhoneSignalScanningTimer.startRunningLocked(this);
}
}
- mPhoneServiceState = state;
+
+ if (mPhoneServiceState != state) {
+ mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_PHONE_STATE_MASK)
+ | (state << BatteryHistoryRecord.STATE_PHONE_STATE_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Phone state " + bin + " to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
+ mPhoneServiceState = state;
+ }
}
public void notePhoneSignalStrengthLocked(SignalStrength signalStrength) {
@@ -1222,6 +1305,11 @@ public final class BatteryStatsImpl extends BatteryStats {
else bin = SIGNAL_STRENGTH_POOR;
}
if (mPhoneSignalStrengthBin != bin) {
+ mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_SIGNAL_STRENGTH_MASK)
+ | (bin << BatteryHistoryRecord.STATE_SIGNAL_STRENGTH_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + bin + " to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
if (mPhoneSignalStrengthBin >= 0) {
mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].stopRunningLocked(this);
}
@@ -1250,6 +1338,11 @@ public final class BatteryStatsImpl extends BatteryStats {
}
if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
if (mPhoneDataConnectionType != bin) {
+ mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_DATA_CONNECTION_MASK)
+ | (bin << BatteryHistoryRecord.STATE_DATA_CONNECTION_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
if (mPhoneDataConnectionType >= 0) {
mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this);
}
@@ -1260,6 +1353,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiOnLocked(int uid) {
if (!mWifiOn) {
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mWifiOn = true;
mWifiOnTimer.startRunningLocked(this);
}
@@ -1274,6 +1371,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiOffLocked(int uid) {
if (mWifiOn) {
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mWifiOn = false;
mWifiOnTimer.stopRunningLocked(this);
}
@@ -1285,6 +1386,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteAudioOnLocked(int uid) {
if (!mAudioOn) {
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_AUDIO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mAudioOn = true;
mAudioOnTimer.startRunningLocked(this);
}
@@ -1293,6 +1398,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteAudioOffLocked(int uid) {
if (mAudioOn) {
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_AUDIO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mAudioOn = false;
mAudioOnTimer.stopRunningLocked(this);
}
@@ -1301,6 +1410,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteVideoOnLocked(int uid) {
if (!mVideoOn) {
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_VIDEO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mVideoOn = true;
mVideoOnTimer.startRunningLocked(this);
}
@@ -1309,6 +1422,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteVideoOffLocked(int uid) {
if (mVideoOn) {
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_VIDEO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mVideoOn = false;
mVideoOnTimer.stopRunningLocked(this);
}
@@ -1317,6 +1434,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiRunningLocked() {
if (!mWifiRunning) {
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_RUNNING_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mWifiRunning = true;
mWifiRunningTimer.startRunningLocked(this);
}
@@ -1324,6 +1445,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteWifiStoppedLocked() {
if (mWifiRunning) {
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_RUNNING_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mWifiRunning = false;
mWifiRunningTimer.stopRunningLocked(this);
}
@@ -1331,6 +1456,10 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteBluetoothOnLocked() {
if (!mBluetoothOn) {
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_BLUETOOTH_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mBluetoothOn = true;
mBluetoothOnTimer.startRunningLocked(this);
}
@@ -1338,32 +1467,84 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteBluetoothOffLocked() {
if (mBluetoothOn) {
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_BLUETOOTH_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
mBluetoothOn = false;
mBluetoothOnTimer.stopRunningLocked(this);
}
}
+ int mWifiFullLockNesting = 0;
+
public void noteFullWifiLockAcquiredLocked(int uid) {
+ if (mWifiFullLockNesting == 0) {
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_FULL_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
+ }
+ mWifiFullLockNesting++;
getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked();
}
public void noteFullWifiLockReleasedLocked(int uid) {
+ mWifiFullLockNesting--;
+ if (mWifiFullLockNesting == 0) {
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_FULL_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
+ }
getUidStatsLocked(uid).noteFullWifiLockReleasedLocked();
}
+ int mWifiScanLockNesting = 0;
+
public void noteScanWifiLockAcquiredLocked(int uid) {
+ if (mWifiScanLockNesting == 0) {
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_SCAN_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan lock on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
+ }
+ mWifiScanLockNesting++;
getUidStatsLocked(uid).noteScanWifiLockAcquiredLocked();
}
public void noteScanWifiLockReleasedLocked(int uid) {
+ mWifiScanLockNesting--;
+ if (mWifiScanLockNesting == 0) {
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_SCAN_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan lock off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
+ }
getUidStatsLocked(uid).noteScanWifiLockReleasedLocked();
}
+ int mWifiMulticastNesting = 0;
+
public void noteWifiMulticastEnabledLocked(int uid) {
+ if (mWifiMulticastNesting == 0) {
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_MULTICAST_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
+ }
+ mWifiMulticastNesting++;
getUidStatsLocked(uid).noteWifiMulticastEnabledLocked();
}
public void noteWifiMulticastDisabledLocked(int uid) {
+ mWifiMulticastNesting--;
+ if (mWifiMulticastNesting == 0) {
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_MULTICAST_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(SystemClock.elapsedRealtime());
+ }
getUidStatsLocked(uid).noteWifiMulticastDisabledLocked();
}
@@ -2766,6 +2947,11 @@ public final class BatteryStatsImpl extends BatteryStats {
}
@Override
+ public BatteryHistoryRecord getHistory() {
+ return mHistory;
+ }
+
+ @Override
public int getStartCount() {
return mStartCount;
}
@@ -2784,6 +2970,12 @@ public final class BatteryStatsImpl extends BatteryStats {
long mSecRealtime = SystemClock.elapsedRealtime();
long realtime = mSecRealtime * 1000;
if (onBattery) {
+ clearHistory();
+ mHistoryCur.batteryLevel = (byte)level;
+ mHistoryCur.states &= ~BatteryHistoryRecord.STATE_BATTERY_PLUGGED_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(mSecRealtime);
mTrackBatteryUptimeStart = uptime;
mTrackBatteryRealtimeStart = realtime;
mUnpluggedBatteryUptime = getBatteryUptimeLocked(uptime);
@@ -2791,6 +2983,11 @@ public final class BatteryStatsImpl extends BatteryStats {
mDischargeCurrentLevel = mDischargeStartLevel = level;
doUnplug(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
} else {
+ mHistoryCur.batteryLevel = (byte)level;
+ mHistoryCur.states |= BatteryHistoryRecord.STATE_BATTERY_PLUGGED_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecord(mSecRealtime);
mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart;
mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart;
mDischargeCurrentLevel = level;
@@ -2806,7 +3003,11 @@ public final class BatteryStatsImpl extends BatteryStats {
}
public void recordCurrentLevel(int level) {
- mDischargeCurrentLevel = level;
+ if (mDischargeCurrentLevel != level) {
+ mDischargeCurrentLevel = level;
+ mHistoryCur.batteryLevel = (byte)level;
+ addHistoryRecord(SystemClock.elapsedRealtime());
+ }
}
public void updateKernelWakelocksLocked() {
@@ -3146,6 +3347,24 @@ public final class BatteryStatsImpl extends BatteryStats {
return 0;
}
+ void readHistory(Parcel in) {
+ mHistory = mHistoryEnd = mHistoryCache = null;
+ long time;
+ while ((time=in.readLong()) >= 0) {
+ BatteryHistoryRecord rec = new BatteryHistoryRecord(time, in);
+ addHistoryRecord(rec);
+ }
+ }
+
+ void writeHistory(Parcel out) {
+ BatteryHistoryRecord rec = mHistory;
+ while (rec != null) {
+ if (rec.time >= 0) rec.writeToParcel(out, 0);
+ rec = rec.next;
+ }
+ out.writeLong(-1);
+ }
+
private void readSummaryFromParcel(Parcel in) {
final int version = in.readInt();
if (version != VERSION) {
@@ -3154,6 +3373,8 @@ public final class BatteryStatsImpl extends BatteryStats {
return;
}
+ readHistory(in);
+
mStartCount = in.readInt();
mBatteryUptime = in.readLong();
mBatteryLastUptime = in.readLong();
@@ -3325,6 +3546,8 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(VERSION);
+ writeHistory(out);
+
out.writeInt(mStartCount);
out.writeLong(computeBatteryUptime(NOW_SYS, STATS_TOTAL));
out.writeLong(computeBatteryUptime(NOW_SYS, STATS_CURRENT));
@@ -3493,6 +3716,8 @@ public final class BatteryStatsImpl extends BatteryStats {
throw new ParcelFormatException("Bad magic number");
}
+ readHistory(in);
+
mStartCount = in.readInt();
mBatteryUptime = in.readLong();
mBatteryLastUptime = in.readLong();
@@ -3591,6 +3816,9 @@ public final class BatteryStatsImpl extends BatteryStats {
final long batteryRealtime = getBatteryRealtimeLocked(uSecRealtime);
out.writeInt(MAGIC);
+
+ writeHistory(out);
+
out.writeInt(mStartCount);
out.writeLong(mBatteryUptime);
out.writeLong(mBatteryLastUptime);
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 599a7fe..59600dc 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -342,6 +342,10 @@ public class RuntimeInit {
mApplicationObject = app;
}
+ public static final IBinder getApplicationObject() {
+ return mApplicationObject;
+ }
+
/**
* Enable debugging features.
*/
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..045c24f
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -0,0 +1,42 @@
+/**
+ * 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 ----
+ // You need the STATUS_BAR_SERVICE permission
+ void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList,
+ out List<IBinder> notificationKeys, out List<StatusBarNotification> notifications);
+ void onPanelRevealed();
+ void onNotificationClick(String pkg, String tag, int id);
+ void onNotificationError(String pkg, String tag, int id, String message);
+ void onClearAllNotifications();
+}
diff --git a/core/java/android/app/IStatusBar.aidl b/core/java/com/android/internal/statusbar/StatusBarIcon.aidl
index c64fa50..311a077 100644
--- a/core/java/android/app/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/StatusBarIcon.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2007, 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.
@@ -13,17 +13,8 @@
* 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);
-}
+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/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/HierarchicalStateMachine.java
index 9911f48..c599d68 100644
--- a/core/java/com/android/internal/util/HierarchicalStateMachine.java
+++ b/core/java/com/android/internal/util/HierarchicalStateMachine.java
@@ -51,7 +51,7 @@ import java.util.HashMap;
mS2 mS1 ----> initial state
</code>
* After the state machine is created and started, messages are sent to a state
- * machine using <code>sendMessage</code and the messages are created using
+ * machine using <code>sendMessage</code> and the messages are created using
* <code>obtainMessage</code>. When the state machine receives a message the
* current state's <code>processMessage</code> is invoked. In the above example
* mS1.processMessage will be invoked first. The state may use <code>transitionTo</code>
@@ -59,9 +59,9 @@ import java.util.HashMap;
*
* Each state in the state machine may have a zero or one parent states and if
* a child state is unable to handle a message it may have the message processed
- * by its parent by returning false. If a message is never processed <code>unhandledMessage</code>
- * will be invoked to give one last chance for the state machine to process
- * the message.
+ * by its parent by returning false or NOT_HANDLED. If a message is never processed
+ * <code>unhandledMessage</code> will be invoked to give one last chance for the state machine
+ * to process the message.
*
* When all processing is completed a state machine may choose to call
* <code>transitionToHaltingState</code>. When the current <code>processingMessage</code>
@@ -95,7 +95,7 @@ import java.util.HashMap;
* any other messages that are on the queue or might be added later. Both of
* these are protected and may only be invoked from within a state machine.
*
- * To illustrate some of these properties we'll use state machine with 8
+ * To illustrate some of these properties we'll use state machine with an 8
* state hierarchy:
<code>
mP0
@@ -109,44 +109,19 @@ import java.util.HashMap;
*
* After starting mS5 the list of active states is mP0, mP1, mS1 and mS5.
* So the order of calling processMessage when a message is received is mS5,
- * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this
- * message by returning false.
+ * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this
+ * message by returning false or NOT_HANDLED.
*
* Now assume mS5.processMessage receives a message it can handle, and during
- * the handling determines the machine should changes states. It would call
- * transitionTo(mS4) and return true. Immediately after returning from
+ * the handling determines the machine should change states. It could call
+ * transitionTo(mS4) and return true or HANDLED. Immediately after returning from
* processMessage the state machine runtime will find the common parent,
* which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then
* mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So
* when the next message is received mS4.processMessage will be invoked.
*
- * To assist in describing an HSM a simple grammar has been created which
- * is informally defined here and a formal EBNF description is at the end
- * of the class comment.
- *
- * An HSM starts with the name and includes a set of hierarchical states.
- * A state is preceeded by one or more plus signs (+), to indicate its
- * depth and a hash (#) if its the initial state. Child states follow their
- * parents and have one more plus sign then their parent. Inside a state
- * are a series of messages, the actions they perform and if the processing
- * is complete ends with a period (.). If processing isn't complete and
- * the parent should process the message it ends with a caret (^). The
- * actions include send a message ($MESSAGE), defer a message (%MESSAGE),
- * transition to a new state (>MESSAGE) and an if statement
- * (if ( expression ) { list of actions }.)
- *
- * The Hsm HelloWorld could documented as:
- *
- * HelloWorld {
- * + # mState1.
- * }
- *
- * and interpreted as HSM HelloWorld:
- *
- * mState1 a root state (single +) and initial state (#) which
- * processes all messages completely, the period (.).
- *
- * The implementation is:
+ * Now for some concrete examples, here is the canonical HelloWorld as an HSM.
+ * It responds with "Hello World" being printed to the log for every message.
<code>
class HelloWorld extends HierarchicalStateMachine {
Hsm1(String name) {
@@ -164,7 +139,7 @@ class HelloWorld extends HierarchicalStateMachine {
class State1 extends HierarchicalState {
@Override public boolean processMessage(Message message) {
Log.d(TAG, "Hello World");
- return true;
+ return HANDLED;
}
}
State1 mState1 = new State1();
@@ -176,7 +151,7 @@ void testHelloWorld() {
}
</code>
*
- * A more interesting state machine is one of four states
+ * A more interesting state machine is one with four states
* with two independent parent states.
<code>
mP1 mP2
@@ -184,45 +159,68 @@ void testHelloWorld() {
mS2 mS1
</code>
*
- * documented as:
+ * Here is a description of this state machine using pseudo code.
*
- * Hsm1 {
- * + mP1 {
- * CMD_2 {
- * $CMD_3
- * %CMD_2
- * >mS2
- * }.
- * }
- * ++ # mS1 { CMD_1{ >mS1 }^ }
- * ++ mS2 {
- * CMD_2{$CMD_4}.
- * CMD_3{%CMD_3 ; >mP2}.
- * }
*
- * + mP2 e($CMD_5) {
- * CMD_3, CMD_4.
- * CMD_5{>HALT}.
- * }
+ * state mP1 {
+ * enter { log("mP1.enter"); }
+ * exit { log("mP1.exit"); }
+ * on msg {
+ * CMD_2 {
+ * send(CMD_3);
+ * defer(msg);
+ * transitonTo(mS2);
+ * return HANDLED;
+ * }
+ * return NOT_HANDLED;
+ * }
* }
*
- * and interpreted as HierarchicalStateMachine Hsm1:
- *
- * mP1 a root state.
- * processes message CMD_2 which sends CMD_3, defers CMD_2, and transitions to mS2
- *
- * mS1 a child of mP1 is the initial state:
- * processes message CMD_1 which transitions to itself and returns false to let mP1 handle it.
+ * INITIAL
+ * state mS1 parent mP1 {
+ * enter { log("mS1.enter"); }
+ * exit { log("mS1.exit"); }
+ * on msg {
+ * CMD_1 {
+ * transitionTo(mS1);
+ * return HANDLED;
+ * }
+ * return NOT_HANDLED;
+ * }
+ * }
*
- * mS2 a child of mP1:
- * processes message CMD_2 which send CMD_4
- * processes message CMD_3 which defers CMD_3 and transitions to mP2
+ * state mS2 parent mP1 {
+ * enter { log("mS2.enter"); }
+ * exit { log("mS2.exit"); }
+ * on msg {
+ * CMD_2 {
+ * send(CMD_4);
+ * return HANDLED;
+ * }
+ * CMD_3 {
+ * defer(msg);
+ * transitionTo(mP2);
+ * return HANDLED;
+ * }
+ * return NOT_HANDLED;
+ * }
+ * }
*
- * mP2 a root state.
- * on enter it sends CMD_5
- * processes message CMD_3
- * processes message CMD_4
- * processes message CMD_5 which transitions to halt state
+ * state mP2 {
+ * enter {
+ * log("mP2.enter");
+ * send(CMD_5);
+ * }
+ * exit { log("mP2.exit"); }
+ * on msg {
+ * CMD_3, CMD_4 { return HANDLED; }
+ * CMD_5 {
+ * transitionTo(HaltingState);
+ * return HANDLED;
+ * }
+ * return NOT_HANDLED;
+ * }
+ * }
*
* The implementation is below and also in HierarchicalStateMachineTest:
<code>
@@ -271,11 +269,11 @@ class Hsm1 extends HierarchicalStateMachine {
sendMessage(obtainMessage(CMD_3));
deferMessage(message);
transitionTo(mS2);
- retVal = true;
+ retVal = HANDLED;
break;
default:
// Any message we don't understand in this state invokes unhandledMessage
- retVal = false;
+ retVal = NOT_HANDLED;
break;
}
return retVal;
@@ -294,10 +292,10 @@ class Hsm1 extends HierarchicalStateMachine {
if (message.what == CMD_1) {
// Transition to ourself to show that enter/exit is called
transitionTo(mS1);
- return true;
+ return HANDLED;
} else {
// Let parent process all other messages
- return false;
+ return NOT_HANDLED;
}
}
@Override public void exit() {
@@ -315,15 +313,15 @@ class Hsm1 extends HierarchicalStateMachine {
switch(message.what) {
case(CMD_2):
sendMessage(obtainMessage(CMD_4));
- retVal = true;
+ retVal = HANDLED;
break;
case(CMD_3):
deferMessage(message);
transitionTo(mP2);
- retVal = true;
+ retVal = HANDLED;
break;
default:
- retVal = false;
+ retVal = NOT_HANDLED;
break;
}
return retVal;
@@ -349,7 +347,7 @@ class Hsm1 extends HierarchicalStateMachine {
transitionToHaltingState();
break;
}
- return true;
+ return HANDLED;
}
@Override public void exit() {
Log.d(TAG, "mP2.exit");
@@ -357,7 +355,7 @@ class Hsm1 extends HierarchicalStateMachine {
}
@Override
- protected void halting() {
+ void halting() {
Log.d(TAG, "halting");
synchronized (this) {
this.notifyAll();
@@ -413,53 +411,32 @@ class Hsm1 extends HierarchicalStateMachine {
* D/hsm1 ( 1999): mP2.exit
* D/hsm1 ( 1999): halting
*
- * Here is the HSM a BNF grammar, this is a first stab at creating an
- * HSM description language, suggestions corrections or alternatives
- * would be much appreciated.
- *
- * Legend:
- * {} ::= zero or more
- * {}+ ::= one or more
- * [] ::= zero or one
- * () ::= define a group with "or" semantics.
- *
- * HSM EBNF:
- * HSM = HSM_NAME "{" { STATE }+ "}" ;
- * HSM_NAME = alpha_numeric_name ;
- * STATE = INTRODUCE_STATE [ ENTER | [ ENTER EXIT ] "{" [ MESSAGES ] "}" [ EXIT ] ;
- * INTRODUCE_STATE = { STATE_DEPTH }+ [ INITIAL_STATE_INDICATOR ] STATE_NAME ;
- * STATE_DEPTH = "+" ;
- * INITIAL_STATE_INDICATOR = "#"
- * ENTER = "e(" SEND_ACTION | TRANSITION_ACTION | HALT_ACTION ")" ;
- * MESSAGES = { MSG_LIST MESSAGE_ACTIONS } ;
- * MSG_LIST = { MSG_NAME { "," MSG_NAME } };
- * EXIT = "x(" SEND_ACTION | TRANSITION_ACTION | HALT_ACTION ")" ;
- * PROCESS_COMPLETION = PROCESS_IN_PARENT_OR_COMPLETE | PROCESS_COMPLETE ;
- * SEND_ACTION = "$" MSG_NAME ;
- * DEFER_ACTION = "%" MSG_NAME ;
- * TRANSITION_ACTION = ">" STATE_NAME ;
- * HALT_ACTION = ">" HALT ;
- * MESSAGE_ACTIONS = { "{" ACTION_LIST "}" } [ PROCESS_COMPLETION ] ;
- * ACTION_LIST = ACTION { (";" | "\n") ACTION } ;
- * ACTION = IF_ACTION | SEND_ACTION | DEFER_ACTION | TRANSITION_ACTION | HALT_ACTION ;
- * IF_ACTION = "if(" boolean_expression ")" "{" ACTION_LIST "}"
- * PROCESS_IN_PARENT_OR_COMPLETE = "^" ;
- * PROCESS_COMPLETE = "." ;
- * STATE_NAME = alpha_numeric_name ;
- * MSG_NAME = alpha_numeric_name | ALL_OTHER_MESSAGES ;
- * ALL_OTHER_MESSAGES = "*" ;
- * EXP = boolean_expression ;
- *
- * Idioms:
- * * { %* }. ::= All other messages will be deferred.
*/
public class HierarchicalStateMachine {
private static final String TAG = "HierarchicalStateMachine";
private String mName;
+ /** Message.what value when quitting */
public static final int HSM_QUIT_CMD = -1;
+ /** Message.what value when initializing */
+ public static final int HSM_INIT_CMD = -1;
+
+ /**
+ * Convenience constant that maybe returned by processMessage
+ * to indicate the the message was processed and is not to be
+ * processed by parent states
+ */
+ public static final boolean HANDLED = true;
+
+ /**
+ * Convenience constant that maybe returned by processMessage
+ * to indicate the the message was NOT processed and is to be
+ * processed by parent states
+ */
+ public static final boolean NOT_HANDLED = false;
+
private static class HsmHandler extends Handler {
/** The debug flag */
@@ -468,6 +445,12 @@ public class HierarchicalStateMachine {
/** The quit object */
private static final Object mQuitObj = new Object();
+ /** The initialization message */
+ private static final Message mInitMsg = null;
+
+ /** The current message */
+ private Message mMsg;
+
/** A list of messages that this state machine has processed */
private ProcessedMessages mProcessedMessages = new ProcessedMessages();
@@ -550,8 +533,7 @@ public class HierarchicalStateMachine {
private class QuittingState extends HierarchicalState {
@Override
public boolean processMessage(Message msg) {
- // Ignore
- return false;
+ return NOT_HANDLED;
}
}
@@ -565,6 +547,9 @@ public class HierarchicalStateMachine {
public final void handleMessage(Message msg) {
if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what);
+ /** Save the current message */
+ mMsg = msg;
+
/**
* Check that construction was completed
*/
@@ -679,6 +664,7 @@ public class HierarchicalStateMachine {
* starting at the first entry.
*/
mIsConstructionCompleted = true;
+ mMsg = obtainMessage(HSM_INIT_CMD);
invokeEnterMethods(0);
/**
@@ -855,6 +841,13 @@ public class HierarchicalStateMachine {
}
/**
+ * @return current message
+ */
+ private final Message getCurrentMessage() {
+ return mMsg;
+ }
+
+ /**
* @return current state
*/
private final HierarchicalState getCurrentState() {
@@ -1025,6 +1018,14 @@ public class HierarchicalStateMachine {
protected final void addState(HierarchicalState state, HierarchicalState parent) {
mHsmHandler.addState(state, parent);
}
+
+ /**
+ * @return current message
+ */
+ protected final Message getCurrentMessage() {
+ return mHsmHandler.getCurrentMessage();
+ }
+
/**
* @return current state
*/
@@ -1032,7 +1033,6 @@ public class HierarchicalStateMachine {
return mHsmHandler.getCurrentState();
}
-
/**
* Add a new state to the state machine, parent will be null
* @param state to add
diff --git a/core/java/com/android/internal/view/BaseSurfaceHolder.java b/core/java/com/android/internal/view/BaseSurfaceHolder.java
index e0d3a5f..3a04993 100644
--- a/core/java/com/android/internal/view/BaseSurfaceHolder.java
+++ b/core/java/com/android/internal/view/BaseSurfaceHolder.java
@@ -33,9 +33,11 @@ public abstract class BaseSurfaceHolder implements SurfaceHolder {
public final ArrayList<SurfaceHolder.Callback> mCallbacks
= new ArrayList<SurfaceHolder.Callback>();
-
+ SurfaceHolder.Callback[] mGottenCallbacks;
+ boolean mHaveGottenCallbacks;
+
public final ReentrantLock mSurfaceLock = new ReentrantLock();
- public final Surface mSurface = new Surface();
+ public Surface mSurface = new Surface();
int mRequestedWidth = -1;
int mRequestedHeight = -1;
@@ -83,6 +85,31 @@ public abstract class BaseSurfaceHolder implements SurfaceHolder {
}
}
+ public SurfaceHolder.Callback[] getCallbacks() {
+ if (mHaveGottenCallbacks) {
+ return mGottenCallbacks;
+ }
+
+ synchronized (mCallbacks) {
+ final int N = mCallbacks.size();
+ if (N > 0) {
+ if (mGottenCallbacks == null || mGottenCallbacks.length != N) {
+ mGottenCallbacks = new SurfaceHolder.Callback[N];
+ }
+ mCallbacks.toArray(mGottenCallbacks);
+ } else {
+ mGottenCallbacks = null;
+ }
+ mHaveGottenCallbacks = true;
+ }
+
+ return mGottenCallbacks;
+ }
+
+ public void ungetCallbacks() {
+ mHaveGottenCallbacks = false;
+ }
+
public void setFixedSize(int width, int height) {
if (mRequestedWidth != width || mRequestedHeight != height) {
mRequestedWidth = width;
diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl
index a05ff14..338dcaa 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -48,4 +48,6 @@ oneway interface IInputMethodSession {
void appPrivateCommand(String action, in Bundle data);
void toggleSoftInput(int showFlags, int hideFlags);
+
+ void finishSession();
}
diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
new file mode 100644
index 0000000..fcb1645
--- /dev/null
+++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
@@ -0,0 +1,11 @@
+package com.android.internal.view;
+
+import android.view.SurfaceHolder;
+
+/** hahahah */
+public interface RootViewSurfaceTaker {
+ SurfaceHolder.Callback willYouTakeTheSurface();
+ void setSurfaceType(int type);
+ void setSurfaceFormat(int format);
+ void setSurfaceKeepScreenOn(boolean keepOn);
+}
diff --git a/core/java/com/android/internal/widget/DigitalClock.java b/core/java/com/android/internal/widget/DigitalClock.java
index 23e2277..fa47ff6 100644
--- a/core/java/com/android/internal/widget/DigitalClock.java
+++ b/core/java/com/android/internal/widget/DigitalClock.java
@@ -30,7 +30,7 @@ import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
import android.widget.TextView;
import java.text.DateFormatSymbols;
@@ -39,7 +39,7 @@ import java.util.Calendar;
/**
* Displays the time
*/
-public class DigitalClock extends RelativeLayout {
+public class DigitalClock extends LinearLayout {
private final static String M12 = "h:mm";
private final static String M24 = "kk:mm";
diff --git a/core/java/com/google/android/mms/ContentType.java b/core/java/com/google/android/mms/ContentType.java
index 94bc9fd..b066fad 100644
--- a/core/java/com/google/android/mms/ContentType.java
+++ b/core/java/com/google/android/mms/ContentType.java
@@ -26,6 +26,7 @@ public class ContentType {
public static final String MMS_GENERIC = "application/vnd.wap.mms-generic";
public static final String MULTIPART_MIXED = "application/vnd.wap.multipart.mixed";
public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related";
+ public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative";
public static final String TEXT_PLAIN = "text/plain";
public static final String TEXT_HTML = "text/html";
diff --git a/core/java/com/google/android/mms/pdu/PduParser.java b/core/java/com/google/android/mms/pdu/PduParser.java
index d465c5a..1cd118b 100644
--- a/core/java/com/google/android/mms/pdu/PduParser.java
+++ b/core/java/com/google/android/mms/pdu/PduParser.java
@@ -200,7 +200,18 @@ public class PduParser {
PduHeaders headers = new PduHeaders();
while (keepParsing && (pduDataStream.available() > 0)) {
+ pduDataStream.mark(1);
int headerField = extractByteValue(pduDataStream);
+ /* parse custom text header */
+ if ((headerField >= TEXT_MIN) && (headerField <= TEXT_MAX)) {
+ pduDataStream.reset();
+ byte [] bVal = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "TextHeader: " + new String(bVal));
+ }
+ /* we should ignore it at the moment */
+ continue;
+ }
switch (headerField) {
case PduHeaders.MESSAGE_TYPE:
{
@@ -778,26 +789,34 @@ public class PduParser {
/* get part's data */
if (dataLength > 0) {
byte[] partData = new byte[dataLength];
+ String partContentType = new String(part.getContentType());
pduDataStream.read(partData, 0, dataLength);
- // Check Content-Transfer-Encoding.
- byte[] partDataEncoding = part.getContentTransferEncoding();
- if (null != partDataEncoding) {
- String encoding = new String(partDataEncoding);
- if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) {
- // Decode "base64" into "binary".
- partData = Base64.decodeBase64(partData);
- } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) {
- // Decode "quoted-printable" into "binary".
- partData = QuotedPrintable.decodeQuotedPrintable(partData);
- } else {
- // "binary" is the default encoding.
+ if (partContentType.equalsIgnoreCase(ContentType.MULTIPART_ALTERNATIVE)) {
+ // parse "multipart/vnd.wap.multipart.alternative".
+ PduBody childBody = parseParts(new ByteArrayInputStream(partData));
+ // take the first part of children.
+ part = childBody.getPart(0);
+ } else {
+ // Check Content-Transfer-Encoding.
+ byte[] partDataEncoding = part.getContentTransferEncoding();
+ if (null != partDataEncoding) {
+ String encoding = new String(partDataEncoding);
+ if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) {
+ // Decode "base64" into "binary".
+ partData = Base64.decodeBase64(partData);
+ } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) {
+ // Decode "quoted-printable" into "binary".
+ partData = QuotedPrintable.decodeQuotedPrintable(partData);
+ } else {
+ // "binary" is the default encoding.
+ }
}
+ if (null == partData) {
+ log("Decode part data error!");
+ return null;
+ }
+ part.setData(partData);
}
- if (null == partData) {
- log("Decode part data error!");
- return null;
- }
- part.setData(partData);
}
/* add this part to body */