diff options
Diffstat (limited to 'core')
245 files changed, 4902 insertions, 1804 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..793b9d2 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -726,6 +726,12 @@ public class ActivityManager { public static final int IMPORTANCE_FOREGROUND = 100; /** + * Constant for {@link #importance}: this process is running a + * heavy-weight application and thus should not be killed. + */ + public static final int IMPORTANCE_HEAVY_WEIGHT = 150; + + /** * Constant for {@link #importance}: this process is running something * that is considered to be actively visible to the user. */ diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index f694285..b78d22f 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1251,6 +1251,13 @@ 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); @@ -2758,5 +2765,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/ContextImpl.java b/core/java/android/app/ContextImpl.java index f471f57..11b7b02 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2173,6 +2173,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..cd24fa6 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -303,6 +303,8 @@ public interface IActivityManager extends IInterface { public boolean isUserAMonkey() throws RemoteException; + public void finishHeavyWeightApp() throws RemoteException; + /* * Private non-Binder interfaces */ @@ -513,4 +515,5 @@ 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; } 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 <application> tag. + */ + public static final int FLAG_HEAVY_WEIGHT = 1<<20; + + /** + * Value for {@link #flags}: this is true if the application has set + * its android:neverEncrypt to true, false otherwise. It is used to specify + * that this package specifically "opts-out" of a secured file system solution, + * 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..025db4a 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,29 @@ public class Camera { private boolean mWithBuffer; /** + * Returns the number of Cameras available. + * @hide + */ + public native static int getNumberOfCameras(); + + /** * Returns a new Camera object. + * If {@link #getNumberOfCameras()} returns N, the valid is is 0 to N-1. + * The id 0 is the default camera. + * @hide + */ + public static Camera open(int cameraId) { + return new Camera(cameraId); + } + + /** + * Returns a new Camera object. This returns the default camera. */ public static Camera open() { - return new Camera(); + return new Camera(0); } - Camera() { + Camera(int cameraId) { mShutterCallback = null; mRawImageCallback = null; mJpegCallback = null; @@ -107,14 +123,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(); @@ -746,6 +762,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 +826,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 +937,56 @@ 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. + * @hide + */ + public static final String METERING_MODE_CENTER_WEIGHTED = "center-weighted"; + + /** + * The camera determines the exposure by averaging the entire scene, + * giving no weighting to any particular area. + * @hide + */ + public static final String METERING_MODE_FRAME_AVERAGE = "frame-average"; + + /** + * The camera determines the exposure by a very small area of the scene, + * typically the center. + * @hide + */ + public static final String METERING_MODE_SPOT = "spot"; + // Formats for setPreviewFormat and setPictureFormat. private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp"; private static final String PIXEL_FORMAT_YUV420SP = "yuv420sp"; @@ -1788,6 +1917,79 @@ 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() + * @hide + */ + public List<String> getSupportedMeteringModes() { + String str = get(KEY_METERING_MODE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** + * Gets the current metering mode, which affects how camera determines + * exposure. + * + * @return current metering mode. If the camera does not support + * metering setting, this should return null. + * @see #METERING_MODE_CENTER_WEIGHTED + * @see #METERING_MODE_FRAME_AVERAGE + * @see #METERING_MODE_SPOT + * @hide + */ + public String getMeteringMode() { + return get(KEY_METERING_MODE); + } + + /** + * Sets the metering mode. + * + * @param value metering mode. + * @see #getMeteringMode() + * @hide + */ + public void setMeteringMode(String value) { + set(KEY_METERING_MODE, value); + } + // Splits a comma delimited string to an ArrayList of String. // Return null if the passing string is null or the size is 0. private ArrayList<String> split(String str) { @@ -1817,6 +2019,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/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/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..ac23832 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -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/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 */ diff --git a/core/jni/Android.mk b/core/jni/Android.mk index df1ab9e..d4545d7 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -28,6 +28,7 @@ LOCAL_SRC_FILES:= \ Time.cpp \ com_google_android_gles_jni_EGLImpl.cpp \ com_google_android_gles_jni_GLImpl.cpp.arm \ + android_app_NativeActivity.cpp \ android_opengl_GLES10.cpp \ android_opengl_GLES10Ext.cpp \ android_opengl_GLES11.cpp \ @@ -50,6 +51,7 @@ LOCAL_SRC_FILES:= \ android_os_Debug.cpp \ android_os_FileUtils.cpp \ android_os_MemoryFile.cpp \ + android_os_MessageQueue.cpp \ android_os_ParcelFileDescriptor.cpp \ android_os_Power.cpp \ android_os_StatFs.cpp \ @@ -123,7 +125,6 @@ LOCAL_SRC_FILES:= \ android_server_Watchdog.cpp \ android_message_digest_sha1.cpp \ android_ddm_DdmHandleNativeHeap.cpp \ - android_location_GpsLocationProvider.cpp \ com_android_internal_os_ZygoteInit.cpp \ com_android_internal_graphics_NativeUtils.cpp \ android_backup_BackupDataInput.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index d38d748..f66ed83 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -128,6 +128,7 @@ extern int register_android_nio_utils(JNIEnv* env); extern int register_android_pim_EventRecurrence(JNIEnv* env); extern int register_android_text_format_Time(JNIEnv* env); extern int register_android_os_Debug(JNIEnv* env); +extern int register_android_os_MessageQueue(JNIEnv* env); extern int register_android_os_ParcelFileDescriptor(JNIEnv *env); extern int register_android_os_Power(JNIEnv *env); extern int register_android_os_StatFs(JNIEnv *env); @@ -156,11 +157,11 @@ extern int register_android_server_BluetoothA2dpService(JNIEnv* env); extern int register_android_server_Watchdog(JNIEnv* env); extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env); -extern int register_android_location_GpsLocationProvider(JNIEnv* env); extern int register_android_backup_BackupDataInput(JNIEnv *env); extern int register_android_backup_BackupDataOutput(JNIEnv *env); extern int register_android_backup_FileBackupHelperBase(JNIEnv *env); extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env); +extern int register_android_app_NativeActivity(JNIEnv *env); static AndroidRuntime* gCurRuntime = NULL; @@ -1250,6 +1251,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_Debug), REG_JNI(register_android_os_FileObserver), REG_JNI(register_android_os_FileUtils), + REG_JNI(register_android_os_MessageQueue), REG_JNI(register_android_os_ParcelFileDescriptor), REG_JNI(register_android_os_Power), REG_JNI(register_android_os_StatFs), @@ -1280,11 +1282,12 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_server_Watchdog), REG_JNI(register_android_message_digest_sha1), REG_JNI(register_android_ddm_DdmHandleNativeHeap), - REG_JNI(register_android_location_GpsLocationProvider), REG_JNI(register_android_backup_BackupDataInput), REG_JNI(register_android_backup_BackupDataOutput), REG_JNI(register_android_backup_FileBackupHelperBase), REG_JNI(register_android_backup_BackupHelperDispatcher), + + REG_JNI(register_android_app_NativeActivity), }; /* diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp new file mode 100644 index 0000000..f2ab134 --- /dev/null +++ b/core/jni/android_app_NativeActivity.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "NativeActivity" +#include <utils/Log.h> + +#include "JNIHelp.h" +#include <android_runtime/AndroidRuntime.h> +#include <android/native_activity.h> + +#include <dlfcn.h> + +namespace android +{ + +struct NativeCode { + NativeCode(void* _dlhandle, android_activity_create_t* _createFunc) { + memset(&activity, sizeof(activity), 0); + memset(&callbacks, sizeof(callbacks), 0); + dlhandle = _dlhandle; + createActivityFunc = _createFunc; + surface = NULL; + } + + ~NativeCode() { + if (callbacks.onDestroy != NULL) { + callbacks.onDestroy(&activity); + } + if (dlhandle != NULL) { + dlclose(dlhandle); + } + } + + void setSurface(jobject _surface) { + if (surface != NULL) { + activity.env->DeleteGlobalRef(surface); + } + if (_surface != NULL) { + surface = activity.env->NewGlobalRef(_surface); + } else { + surface = NULL; + } + } + + android_activity_t activity; + android_activity_callbacks_t callbacks; + + void* dlhandle; + android_activity_create_t* createActivityFunc; + + jobject surface; +}; + +static jint +loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path) +{ + const char* pathStr = env->GetStringUTFChars(path, NULL); + NativeCode* code = NULL; + + void* handle = dlopen(pathStr, RTLD_LAZY); + + env->ReleaseStringUTFChars(path, pathStr); + + if (handle != NULL) { + code = new NativeCode(handle, (android_activity_create_t*) + dlsym(handle, "android_onCreateActivity")); + if (code->createActivityFunc == NULL) { + LOGW("android_onCreateActivity not found"); + delete code; + return 0; + } + code->activity.callbacks = &code->callbacks; + code->activity.env = env; + code->activity.clazz = clazz; + code->createActivityFunc(&code->activity, NULL, 0); + } + + return (jint)code; +} + +static void +unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle) +{ + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + delete code; + } +} + +static void +onStart_native(JNIEnv* env, jobject clazz, jint handle) +{ + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->callbacks.onStart != NULL) { + code->callbacks.onStart(&code->activity); + } + } +} + +static void +onResume_native(JNIEnv* env, jobject clazz, jint handle) +{ + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->callbacks.onResume != NULL) { + code->callbacks.onResume(&code->activity); + } + } +} + +static void +onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle) +{ + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->callbacks.onSaveInstanceState != NULL) { + size_t len = 0; + code->callbacks.onSaveInstanceState(&code->activity, &len); + } + } +} + +static void +onPause_native(JNIEnv* env, jobject clazz, jint handle) +{ + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->callbacks.onPause != NULL) { + code->callbacks.onPause(&code->activity); + } + } +} + +static void +onStop_native(JNIEnv* env, jobject clazz, jint handle) +{ + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->callbacks.onStop != NULL) { + code->callbacks.onStop(&code->activity); + } + } +} + +static void +onLowMemory_native(JNIEnv* env, jobject clazz, jint handle) +{ + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->callbacks.onLowMemory != NULL) { + code->callbacks.onLowMemory(&code->activity); + } + } +} + +static void +onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused) +{ + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->callbacks.onWindowFocusChanged != NULL) { + code->callbacks.onWindowFocusChanged(&code->activity, focused ? 1 : 0); + } + } +} + +static void +onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) +{ + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + code->setSurface(surface); + if (code->callbacks.onSurfaceCreated != NULL) { + code->callbacks.onSurfaceCreated(&code->activity, + (android_surface_t*)code->surface); + } + } +} + +static void +onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface, + jint format, jint width, jint height) +{ + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->surface != NULL && code->callbacks.onSurfaceChanged != NULL) { + code->callbacks.onSurfaceChanged(&code->activity, + (android_surface_t*)code->surface, format, width, height); + } + } +} + +static void +onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) +{ + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->surface != NULL && code->callbacks.onSurfaceDestroyed != NULL) { + code->callbacks.onSurfaceDestroyed(&code->activity, + (android_surface_t*)code->surface); + } + code->setSurface(NULL); + } +} + +static const JNINativeMethod g_methods[] = { + { "loadNativeCode", "(Ljava/lang/String;)I", (void*)loadNativeCode_native }, + { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native }, + { "onStartNative", "(I)V", (void*)onStart_native }, + { "onResumeNative", "(I)V", (void*)onResume_native }, + { "onSaveInstanceStateNative", "(I)V", (void*)onSaveInstanceState_native }, + { "onPauseNative", "(I)V", (void*)onPause_native }, + { "onStopNative", "(I)V", (void*)onStop_native }, + { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native }, + { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native }, + { "onSurfaceCreatedNative", "(ILandroid/view/SurfaceHolder;)V", (void*)onSurfaceCreated_native }, + { "onSurfaceChangedNative", "(ILandroid/view/SurfaceHolder;III)V", (void*)onSurfaceChanged_native }, + { "onSurfaceDestroyedNative", "(ILandroid/view/SurfaceHolder;)V", (void*)onSurfaceDestroyed_native }, +}; + +static const char* const kNativeActivityPathName = "android/app/NativeActivity"; + +int register_android_app_NativeActivity(JNIEnv* env) +{ + //LOGD("register_android_app_NativeActivity"); + + jclass clazz; + + clazz = env->FindClass(kNativeActivityPathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.NativeActivity"); + + return AndroidRuntime::registerNativeMethods( + env, kNativeActivityPathName, + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_bluetooth_HeadsetBase.cpp b/core/jni/android_bluetooth_HeadsetBase.cpp index b0b0cb8..5593a26 100644 --- a/core/jni/android_bluetooth_HeadsetBase.cpp +++ b/core/jni/android_bluetooth_HeadsetBase.cpp @@ -169,7 +169,7 @@ again: // never receive non-ASCII UTF-8). // This was added because of the BMW 2005 E46 which sends binary junk. if (is_ascii(buf)) { - LOG(LOG_INFO, "Bluetooth AT recv", buf); + LOG(LOG_INFO, "Bluetooth AT recv", "%s", buf); } else { LOGW("Ignoring invalid AT command: %s", buf); buf[0] = NULL; @@ -494,7 +494,7 @@ static void pretty_log_urc(const char *urc) { } } } - LOG(LOG_INFO, "Bluetooth AT sent", buf); + LOG(LOG_INFO, "Bluetooth AT sent", "%s", buf); free(buf); } diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp index 343fa53..9a8f1b8 100644 --- a/core/jni/android_bluetooth_common.cpp +++ b/core/jni/android_bluetooth_common.cpp @@ -65,6 +65,7 @@ static Properties adapter_properties[] = { {"PairableTimeout", DBUS_TYPE_UINT32}, {"Discovering", DBUS_TYPE_BOOLEAN}, {"Devices", DBUS_TYPE_ARRAY}, + {"UUIDs", DBUS_TYPE_ARRAY}, }; typedef union { diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index b85466b..c363156 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -288,10 +288,16 @@ void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env) } } +static jint android_hardware_Camera_getNumberOfCameras(JNIEnv *env, jobject thiz) +{ + return Camera::getNumberOfCameras(); +} + // connect to camera service -static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) +static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, + jobject weak_this, jint cameraId) { - sp<Camera> camera = Camera::connect(); + sp<Camera> camera = Camera::connect(cameraId); if (camera == NULL) { jniThrowException(env, "java/lang/RuntimeException", @@ -566,8 +572,11 @@ static void android_hardware_Camera_setDisplayOrientation(JNIEnv *env, jobject t //------------------------------------------------- static JNINativeMethod camMethods[] = { + { "getNumberOfCameras", + "()I", + (void *)android_hardware_Camera_getNumberOfCameras }, { "native_setup", - "(Ljava/lang/Object;)V", + "(Ljava/lang/Object;I)V", (void*)android_hardware_Camera_native_setup }, { "native_release", "()V", @@ -659,7 +668,7 @@ int register_android_hardware_Camera(JNIEnv *env) { field fields_to_find[] = { { "android/hardware/Camera", "mNativeContext", "I", &fields.context }, - { "android/view/Surface", "mSurface", "I", &fields.surface } + { "android/view/Surface", ANDROID_VIEW_SURFACE_JNI_ID, "I", &fields.surface } }; if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0) diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp deleted file mode 100755 index f60fe6d..0000000 --- a/core/jni/android_location_GpsLocationProvider.cpp +++ /dev/null @@ -1,542 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "GpsLocationProvider" - -//#define LOG_NDDEBUG 0 - -#include "JNIHelp.h" -#include "jni.h" -#include "hardware_legacy/gps.h" -#include "hardware_legacy/gps_ni.h" -#include "utils/Log.h" -#include "utils/misc.h" - -#include <string.h> -#include <pthread.h> - -static pthread_mutex_t sEventMutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t sEventCond = PTHREAD_COND_INITIALIZER; -static jmethodID method_reportLocation; -static jmethodID method_reportStatus; -static jmethodID method_reportSvStatus; -static jmethodID method_reportAGpsStatus; -static jmethodID method_reportNmea; -static jmethodID method_xtraDownloadRequest; -static jmethodID method_reportNiNotification; - -static const GpsInterface* sGpsInterface = NULL; -static const GpsXtraInterface* sGpsXtraInterface = NULL; -static const AGpsInterface* sAGpsInterface = NULL; -static const GpsPrivacyInterface* sGpsPrivacyInterface = NULL; -static const GpsNiInterface* sGpsNiInterface = NULL; -static const GpsDebugInterface* sGpsDebugInterface = NULL; - -// data written to by GPS callbacks -static GpsLocation sGpsLocation; -static GpsStatus sGpsStatus; -static GpsSvStatus sGpsSvStatus; -static AGpsStatus sAGpsStatus; -static GpsNiNotification sGpsNiNotification; - -// buffer for NMEA data -#define NMEA_SENTENCE_LENGTH 100 -#define NMEA_SENTENCE_COUNT 40 -struct NmeaSentence { - GpsUtcTime timestamp; - char nmea[NMEA_SENTENCE_LENGTH]; -}; -static NmeaSentence sNmeaBuffer[NMEA_SENTENCE_COUNT]; -static int mNmeaSentenceCount = 0; - -// a copy of the data shared by android_location_GpsLocationProvider_wait_for_event -// and android_location_GpsLocationProvider_read_status -static GpsLocation sGpsLocationCopy; -static GpsStatus sGpsStatusCopy; -static GpsSvStatus sGpsSvStatusCopy; -static AGpsStatus sAGpsStatusCopy; -static NmeaSentence sNmeaBufferCopy[NMEA_SENTENCE_COUNT]; -static GpsNiNotification sGpsNiNotificationCopy; - -enum CallbackType { - kLocation = 1, - kStatus = 2, - kSvStatus = 4, - kAGpsStatus = 8, - kXtraDownloadRequest = 16, - kDisableRequest = 32, - kNmeaAvailable = 64, - kNiNotification = 128, -}; -static int sPendingCallbacks; - -namespace android { - -static void location_callback(GpsLocation* location) -{ - pthread_mutex_lock(&sEventMutex); - - sPendingCallbacks |= kLocation; - memcpy(&sGpsLocation, location, sizeof(sGpsLocation)); - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); -} - -static void status_callback(GpsStatus* status) -{ - pthread_mutex_lock(&sEventMutex); - - sPendingCallbacks |= kStatus; - memcpy(&sGpsStatus, status, sizeof(sGpsStatus)); - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); -} - -static void sv_status_callback(GpsSvStatus* sv_status) -{ - pthread_mutex_lock(&sEventMutex); - - sPendingCallbacks |= kSvStatus; - memcpy(&sGpsSvStatus, sv_status, sizeof(GpsSvStatus)); - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); -} - -static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length) -{ - pthread_mutex_lock(&sEventMutex); - - if (length >= NMEA_SENTENCE_LENGTH) { - LOGE("NMEA data too long in nmea_callback (length = %d)\n", length); - length = NMEA_SENTENCE_LENGTH - 1; - } - if (mNmeaSentenceCount >= NMEA_SENTENCE_COUNT) { - LOGE("NMEA data overflowed buffer\n"); - pthread_mutex_unlock(&sEventMutex); - return; - } - - sPendingCallbacks |= kNmeaAvailable; - sNmeaBuffer[mNmeaSentenceCount].timestamp = timestamp; - memcpy(sNmeaBuffer[mNmeaSentenceCount].nmea, nmea, length); - sNmeaBuffer[mNmeaSentenceCount].nmea[length] = 0; - mNmeaSentenceCount++; - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); -} - -static void agps_status_callback(AGpsStatus* agps_status) -{ - pthread_mutex_lock(&sEventMutex); - - sPendingCallbacks |= kAGpsStatus; - memcpy(&sAGpsStatus, agps_status, sizeof(AGpsStatus)); - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); -} - -GpsCallbacks sGpsCallbacks = { - location_callback, - status_callback, - sv_status_callback, - nmea_callback -}; - -static void -download_request_callback() -{ - pthread_mutex_lock(&sEventMutex); - sPendingCallbacks |= kXtraDownloadRequest; - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); -} - -static void -gps_ni_notify_callback(GpsNiNotification *notification) -{ - LOGD("gps_ni_notify_callback: notif=%d", notification->notification_id); - - pthread_mutex_lock(&sEventMutex); - - sPendingCallbacks |= kNiNotification; - memcpy(&sGpsNiNotification, notification, sizeof(GpsNiNotification)); - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); -} - -GpsXtraCallbacks sGpsXtraCallbacks = { - download_request_callback, -}; - -AGpsCallbacks sAGpsCallbacks = { - agps_status_callback, -}; - -GpsNiCallbacks sGpsNiCallbacks = { - gps_ni_notify_callback, -}; - -static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { - method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V"); - method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); - method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V"); - method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II)V"); - method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(IJ)V"); - method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V"); - method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V"); -} - -static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) { - if (!sGpsInterface) - sGpsInterface = gps_get_interface(); - return (sGpsInterface != NULL); -} - -static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj) -{ - if (!sGpsInterface) - sGpsInterface = gps_get_interface(); - if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0) - return false; - - if (!sAGpsInterface) - sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); - if (sAGpsInterface) - sAGpsInterface->init(&sAGpsCallbacks); - - if (!sGpsNiInterface) - sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE); - if (sGpsNiInterface) - sGpsNiInterface->init(&sGpsNiCallbacks); - - // Clear privacy lock while enabled - if (!sGpsPrivacyInterface) - sGpsPrivacyInterface = (const GpsPrivacyInterface*)sGpsInterface->get_extension(GPS_PRIVACY_INTERFACE); - if (sGpsPrivacyInterface) - sGpsPrivacyInterface->set_privacy_lock(0); - - if (!sGpsDebugInterface) - sGpsDebugInterface = (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE); - - return true; -} - -static void android_location_GpsLocationProvider_disable(JNIEnv* env, jobject obj) -{ - // Enable privacy lock while disabled - if (!sGpsPrivacyInterface) - sGpsPrivacyInterface = (const GpsPrivacyInterface*)sGpsInterface->get_extension(GPS_PRIVACY_INTERFACE); - if (sGpsPrivacyInterface) - sGpsPrivacyInterface->set_privacy_lock(1); - - pthread_mutex_lock(&sEventMutex); - sPendingCallbacks |= kDisableRequest; - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); -} - -static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject obj) -{ - sGpsInterface->cleanup(); -} - -static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj, jint positionMode, - jboolean singleFix, jint fixFrequency) -{ - int result = sGpsInterface->set_position_mode(positionMode, (singleFix ? 0 : fixFrequency)); - if (result) { - return false; - } - - return (sGpsInterface->start() == 0); -} - -static jboolean android_location_GpsLocationProvider_stop(JNIEnv* env, jobject obj) -{ - return (sGpsInterface->stop() == 0); -} - -static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* env, jobject obj, jint flags) -{ - sGpsInterface->delete_aiding_data(flags); -} - -static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, jobject obj) -{ - pthread_mutex_lock(&sEventMutex); - while (sPendingCallbacks == 0) { - pthread_cond_wait(&sEventCond, &sEventMutex); - } - - // copy and clear the callback flags - int pendingCallbacks = sPendingCallbacks; - sPendingCallbacks = 0; - int nmeaSentenceCount = mNmeaSentenceCount; - mNmeaSentenceCount = 0; - - // copy everything and unlock the mutex before calling into Java code to avoid the possibility - // of timeouts in the GPS engine. - if (pendingCallbacks & kLocation) - memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy)); - if (pendingCallbacks & kStatus) - memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy)); - if (pendingCallbacks & kSvStatus) - memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy)); - if (pendingCallbacks & kAGpsStatus) - memcpy(&sAGpsStatusCopy, &sAGpsStatus, sizeof(sAGpsStatusCopy)); - if (pendingCallbacks & kNmeaAvailable) - memcpy(&sNmeaBufferCopy, &sNmeaBuffer, nmeaSentenceCount * sizeof(sNmeaBuffer[0])); - if (pendingCallbacks & kNiNotification) - memcpy(&sGpsNiNotificationCopy, &sGpsNiNotification, sizeof(sGpsNiNotificationCopy)); - pthread_mutex_unlock(&sEventMutex); - - if (pendingCallbacks & kLocation) { - env->CallVoidMethod(obj, method_reportLocation, sGpsLocationCopy.flags, - (jdouble)sGpsLocationCopy.latitude, (jdouble)sGpsLocationCopy.longitude, - (jdouble)sGpsLocationCopy.altitude, - (jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing, - (jfloat)sGpsLocationCopy.accuracy, (jlong)sGpsLocationCopy.timestamp); - } - if (pendingCallbacks & kStatus) { - env->CallVoidMethod(obj, method_reportStatus, sGpsStatusCopy.status); - } - if (pendingCallbacks & kSvStatus) { - env->CallVoidMethod(obj, method_reportSvStatus); - } - if (pendingCallbacks & kAGpsStatus) { - env->CallVoidMethod(obj, method_reportAGpsStatus, sAGpsStatusCopy.type, sAGpsStatusCopy.status); - } - if (pendingCallbacks & kNmeaAvailable) { - for (int i = 0; i < nmeaSentenceCount; i++) { - env->CallVoidMethod(obj, method_reportNmea, i, sNmeaBuffer[i].timestamp); - } - } - if (pendingCallbacks & kXtraDownloadRequest) { - env->CallVoidMethod(obj, method_xtraDownloadRequest); - } - if (pendingCallbacks & kDisableRequest) { - // don't need to do anything - we are just poking so wait_for_event will return. - } - if (pendingCallbacks & kNiNotification) { - LOGD("android_location_GpsLocationProvider_wait_for_event: sent notification callback."); - jstring reqId = env->NewStringUTF(sGpsNiNotificationCopy.requestor_id); - jstring text = env->NewStringUTF(sGpsNiNotificationCopy.text); - jstring extras = env->NewStringUTF(sGpsNiNotificationCopy.extras); - env->CallVoidMethod(obj, method_reportNiNotification, - sGpsNiNotificationCopy.notification_id, - sGpsNiNotificationCopy.ni_type, - sGpsNiNotificationCopy.notify_flags, - sGpsNiNotificationCopy.timeout, - sGpsNiNotificationCopy.default_response, - reqId, - text, - sGpsNiNotificationCopy.requestor_id_encoding, - sGpsNiNotificationCopy.text_encoding, - extras - ); - } -} - -static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj, - jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray, - jintArray maskArray) -{ - // this should only be called from within a call to reportStatus, so we don't need to lock here - - jint* prns = env->GetIntArrayElements(prnArray, 0); - jfloat* snrs = env->GetFloatArrayElements(snrArray, 0); - jfloat* elev = env->GetFloatArrayElements(elevArray, 0); - jfloat* azim = env->GetFloatArrayElements(azumArray, 0); - jint* mask = env->GetIntArrayElements(maskArray, 0); - - int num_svs = sGpsSvStatusCopy.num_svs; - for (int i = 0; i < num_svs; i++) { - prns[i] = sGpsSvStatusCopy.sv_list[i].prn; - snrs[i] = sGpsSvStatusCopy.sv_list[i].snr; - elev[i] = sGpsSvStatusCopy.sv_list[i].elevation; - azim[i] = sGpsSvStatusCopy.sv_list[i].azimuth; - } - mask[0] = sGpsSvStatusCopy.ephemeris_mask; - mask[1] = sGpsSvStatusCopy.almanac_mask; - mask[2] = sGpsSvStatusCopy.used_in_fix_mask; - - env->ReleaseIntArrayElements(prnArray, prns, 0); - env->ReleaseFloatArrayElements(snrArray, snrs, 0); - env->ReleaseFloatArrayElements(elevArray, elev, 0); - env->ReleaseFloatArrayElements(azumArray, azim, 0); - env->ReleaseIntArrayElements(maskArray, mask, 0); - return num_svs; -} - -static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj, jint index, jbyteArray nmeaArray, jint buffer_size) -{ - // this should only be called from within a call to reportNmea, so we don't need to lock here - - jbyte* nmea = env->GetByteArrayElements(nmeaArray, 0); - - int length = strlen(sNmeaBufferCopy[index].nmea); - if (length > buffer_size) - length = buffer_size; - memcpy(nmea, sNmeaBufferCopy[index].nmea, length); - - env->ReleaseByteArrayElements(nmeaArray, nmea, 0); - return length; -} - -static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj, jlong time, - jlong timeReference, jint uncertainty) -{ - sGpsInterface->inject_time(time, timeReference, uncertainty); -} - -static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj, - jdouble latitude, jdouble longitude, jfloat accuracy) -{ - sGpsInterface->inject_location(latitude, longitude, accuracy); -} - -static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj) -{ - if (!sGpsXtraInterface) { - sGpsXtraInterface = (const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE); - if (sGpsXtraInterface) { - int result = sGpsXtraInterface->init(&sGpsXtraCallbacks); - if (result) { - sGpsXtraInterface = NULL; - } - } - } - - return (sGpsXtraInterface != NULL); -} - -static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj, - jbyteArray data, jint length) -{ - jbyte* bytes = env->GetByteArrayElements(data, 0); - sGpsXtraInterface->inject_xtra_data((char *)bytes, length); - env->ReleaseByteArrayElements(data, bytes, 0); -} - -static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env, jobject obj, jstring apn) -{ - if (!sAGpsInterface) { - sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); - } - if (sAGpsInterface) { - if (apn == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return; - } - const char *apnStr = env->GetStringUTFChars(apn, NULL); - sAGpsInterface->data_conn_open(apnStr); - env->ReleaseStringUTFChars(apn, apnStr); - } -} - -static void android_location_GpsLocationProvider_agps_data_conn_closed(JNIEnv* env, jobject obj) -{ - if (!sAGpsInterface) { - sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); - } - if (sAGpsInterface) { - sAGpsInterface->data_conn_closed(); - } -} - -static void android_location_GpsLocationProvider_agps_data_conn_failed(JNIEnv* env, jobject obj) -{ - if (!sAGpsInterface) { - sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); - } - if (sAGpsInterface) { - sAGpsInterface->data_conn_failed(); - } -} - -static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jobject obj, - jint type, jstring hostname, jint port) -{ - if (!sAGpsInterface) { - sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); - } - if (sAGpsInterface) { - const char *c_hostname = env->GetStringUTFChars(hostname, NULL); - sAGpsInterface->set_server(type, c_hostname, port); - env->ReleaseStringUTFChars(hostname, c_hostname); - } -} - -static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj, - jint notifId, jint response) -{ - if (!sGpsNiInterface) - sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE); - if (sGpsNiInterface) - sGpsNiInterface->respond(notifId, response); -} - -static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* env, jobject obj) -{ - jstring result = NULL; - if (sGpsDebugInterface) { - const size_t maxLength = 2047; - char buffer[maxLength+1]; - size_t length = sGpsDebugInterface->get_internal_state(buffer, maxLength); - if (length > maxLength) length = maxLength; - buffer[length] = 0; - result = env->NewStringUTF(buffer); - } - return result; -} - -static JNINativeMethod sMethods[] = { - /* name, signature, funcPtr */ - {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native}, - {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported}, - {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init}, - {"native_disable", "()V", (void*)android_location_GpsLocationProvider_disable}, - {"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup}, - {"native_start", "(IZI)Z", (void*)android_location_GpsLocationProvider_start}, - {"native_stop", "()Z", (void*)android_location_GpsLocationProvider_stop}, - {"native_delete_aiding_data", "(I)V", (void*)android_location_GpsLocationProvider_delete_aiding_data}, - {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event}, - {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status}, - {"native_read_nmea", "(I[BI)I", (void*)android_location_GpsLocationProvider_read_nmea}, - {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time}, - {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location}, - {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, - {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data}, - {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open}, - {"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed}, - {"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed}, - {"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server}, - {"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response}, - {"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state}, -}; - -int register_android_location_GpsLocationProvider(JNIEnv* env) -{ - return jniRegisterNativeMethods(env, "com/android/internal/location/GpsLocationProvider", sMethods, NELEM(sMethods)); -} - -} /* namespace android */ diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp new file mode 100644 index 0000000..8984057 --- /dev/null +++ b/core/jni/android_os_MessageQueue.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "MQNative" + +#include "JNIHelp.h" + +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/time.h> +#include <fcntl.h> + +#include <android_runtime/AndroidRuntime.h> +#include <utils/SystemClock.h> +#include <utils/Vector.h> +#include <utils/Log.h> + +using namespace android; + +// ---------------------------------------------------------------------------- + +static struct { + jclass mClass; + + jfieldID mObject; // native object attached to the DVM MessageQueue +} gMessageQueueOffsets; + +static struct { + jclass mClass; + jmethodID mConstructor; +} gKeyEventOffsets; + +// TODO: also MotionEvent offsets etc. a la gKeyEventOffsets + +static struct { + jclass mClass; + jmethodID mObtain; // obtain(Handler h, int what, Object obj) +} gMessageOffsets; + +// ---------------------------------------------------------------------------- + +static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + if (jniThrowException(env, exc, msg) != 0) + assert(false); +} + +// ---------------------------------------------------------------------------- + +class MessageQueueNative { +public: + MessageQueueNative(int readSocket, int writeSocket); + ~MessageQueueNative(); + + // select on all FDs until the designated time; forever if wakeupTime is < 0 + int waitForSignal(jobject mqueue, jlong wakeupTime); + + // signal the queue-ready pipe + void signalQueuePipe(); + + // Specify a new input pipe, passing in responsibility for the socket fd and + // ashmem region + int registerInputPipe(JNIEnv* env, int socketFd, int memRegionFd, jobject handler); + + // Forget about this input pipe, closing the socket and ashmem region as well + int unregisterInputPipe(JNIEnv* env, int socketFd); + + size_t numRegisteredPipes() const { return mInputPipes.size(); } + +private: + struct InputPipe { + int fd; + int region; + jobject handler; + + InputPipe() {} + InputPipe(int _fd, int _r, jobject _h) : fd(_fd), region(_r), handler(_h) {} + }; + + // consume an event from a socket, put it on the DVM MessageQueue indicated, + // and notify the other end of the pipe that we've consumed it. + void queueEventFromPipe(const InputPipe& pipe, jobject mqueue); + + int mQueueReadFd, mQueueWriteFd; + Vector<InputPipe> mInputPipes; +}; + +MessageQueueNative::MessageQueueNative(int readSocket, int writeSocket) + : mQueueReadFd(readSocket), mQueueWriteFd(writeSocket) { +} + +MessageQueueNative::~MessageQueueNative() { +} + +int MessageQueueNative::waitForSignal(jobject mqueue, jlong timeoutMillis) { + struct timeval tv, *timeout; + fd_set fdset; + + if (timeoutMillis < 0) { + timeout = NULL; + } else { + if (timeoutMillis == 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } else { + tv.tv_sec = (timeoutMillis / 1000); + tv.tv_usec = (timeoutMillis - (1000 * tv.tv_sec)) * 1000; + } + timeout = &tv; + } + + // always rebuild the fd set from scratch + FD_ZERO(&fdset); + + // the queue signalling pipe + FD_SET(mQueueReadFd, &fdset); + int maxFd = mQueueReadFd; + + // and the input sockets themselves + for (size_t i = 0; i < mInputPipes.size(); i++) { + FD_SET(mInputPipes[i].fd, &fdset); + if (maxFd < mInputPipes[i].fd) { + maxFd = mInputPipes[i].fd; + } + } + + // now wait + int res = select(maxFd + 1, &fdset, NULL, NULL, timeout); + + // Error? Just return it and bail + if (res < 0) return res; + + // What happened -- timeout or actual data arrived? + if (res == 0) { + // select() returned zero, which means we timed out, which means that it's time + // to deliver the head element that was already on the queue. Just fall through + // without doing anything else. + } else { + // Data (or a queue signal) arrived! + // + // If it's data, pull the data off the pipe, build a new Message with it, put it on + // the DVM-side MessageQueue (pointed to by the 'mqueue' parameter), then proceed + // into the queue-signal case. + // + // If a queue signal arrived, just consume any data pending in that pipe and + // fall out. + bool queue_signalled = (FD_ISSET(mQueueReadFd, &fdset) != 0); + + for (size_t i = 0; i < mInputPipes.size(); i++) { + if (FD_ISSET(mInputPipes[i].fd, &fdset)) { + queueEventFromPipe(mInputPipes[i], mqueue); + queue_signalled = true; // we know a priori that queueing the event does this + } + } + + // Okay, stuff went on the queue. Consume the contents of the signal pipe + // now that we're awake and about to start dispatching messages again. + if (queue_signalled) { + uint8_t buf[16]; + ssize_t nRead; + do { + nRead = read(mQueueReadFd, buf, sizeof(buf)); + } while (nRead > 0); // in nonblocking mode we'll get -1 when it's drained + } + } + + return 0; +} + +// signals to the queue pipe are one undefined byte. it's just a "data has arrived" token +// and the pipe is drained on receipt of at least one signal +void MessageQueueNative::signalQueuePipe() { + int dummy[1]; + write(mQueueWriteFd, dummy, 1); +} + +void MessageQueueNative::queueEventFromPipe(const InputPipe& inPipe, jobject mqueue) { + // !!! TODO: read the event data from the InputPipe's ashmem region, convert it to a DVM + // event object of the proper type [MotionEvent or KeyEvent], create a Message holding + // it as appropriate, point the Message to the Handler associated with this InputPipe, + // and call up to the DVM MessageQueue implementation to enqueue it for delivery. +} + +// the number of registered pipes on success; < 0 on error +int MessageQueueNative::registerInputPipe(JNIEnv* env, + int socketFd, int memRegionFd, jobject handler) { + // make sure this fd is not already known to us + for (size_t i = 0; i < mInputPipes.size(); i++) { + if (mInputPipes[i].fd == socketFd) { + LOGE("Attempt to re-register input fd %d", socketFd); + return -1; + } + } + + mInputPipes.push( InputPipe(socketFd, memRegionFd, env->NewGlobalRef(handler)) ); + return mInputPipes.size(); +} + +// Remove an input pipe from our bookkeeping. Also closes the socket and ashmem +// region file descriptor! +// +// returns the number of remaining input pipes on success; < 0 on error +int MessageQueueNative::unregisterInputPipe(JNIEnv* env, int socketFd) { + for (size_t i = 0; i < mInputPipes.size(); i++) { + if (mInputPipes[i].fd == socketFd) { + close(mInputPipes[i].fd); + close(mInputPipes[i].region); + env->DeleteGlobalRef(mInputPipes[i].handler); + mInputPipes.removeAt(i); + return mInputPipes.size(); + } + } + LOGW("Tried to unregister input pipe %d but not found!", socketFd); + return -1; +} + +// ---------------------------------------------------------------------------- + +namespace android { + +static void android_os_MessageQueue_init(JNIEnv* env, jobject obj) { + // Create the pipe + int fds[2]; + int err = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds); + if (err != 0) { + doThrow(env, "java/lang/RuntimeException", "Unable to create socket pair"); + } + + MessageQueueNative *mqn = new MessageQueueNative(fds[0], fds[1]); + if (mqn == NULL) { + close(fds[0]); + close(fds[1]); + doThrow(env, "java/lang/RuntimeException", "Unable to allocate native queue"); + } + + int flags = fcntl(fds[0], F_GETFL); + fcntl(fds[0], F_SETFL, flags | O_NONBLOCK); + flags = fcntl(fds[1], F_GETFL); + fcntl(fds[1], F_SETFL, flags | O_NONBLOCK); + + env->SetIntField(obj, gMessageQueueOffsets.mObject, (jint)mqn); +} + +static void android_os_MessageQueue_signal(JNIEnv* env, jobject obj) { + MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject); + if (mqn != NULL) { + mqn->signalQueuePipe(); + } else { + doThrow(env, "java/lang/IllegalStateException", "Queue not initialized"); + } +} + +static int android_os_MessageQueue_waitForNext(JNIEnv* env, jobject obj, jlong when) { + MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject); + if (mqn != NULL) { + int res = mqn->waitForSignal(obj, when); + return res; // the DVM event, if any, has been constructed and queued now + } + + return -1; +} + +static void android_os_MessageQueue_registerInputStream(JNIEnv* env, jobject obj, + jint socketFd, jint regionFd, jobject handler) { + MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject); + if (mqn != NULL) { + mqn->registerInputPipe(env, socketFd, regionFd, handler); + } else { + doThrow(env, "java/lang/IllegalStateException", "Queue not initialized"); + } +} + +static void android_os_MessageQueue_unregisterInputStream(JNIEnv* env, jobject obj, + jint socketFd) { + MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject); + if (mqn != NULL) { + mqn->unregisterInputPipe(env, socketFd); + } else { + doThrow(env, "java/lang/IllegalStateException", "Queue not initialized"); + } +} + +// ---------------------------------------------------------------------------- + +const char* const kKeyEventPathName = "android/view/KeyEvent"; +const char* const kMessagePathName = "android/os/Message"; +const char* const kMessageQueuePathName = "android/os/MessageQueue"; + +static JNINativeMethod gMessageQueueMethods[] = { + /* name, signature, funcPtr */ + { "nativeInit", "()V", (void*)android_os_MessageQueue_init }, + { "nativeSignal", "()V", (void*)android_os_MessageQueue_signal }, + { "nativeWaitForNext", "(J)I", (void*)android_os_MessageQueue_waitForNext }, + { "nativeRegisterInputStream", "(IILandroid/os/Handler;)V", (void*)android_os_MessageQueue_registerInputStream }, + { "nativeUnregisterInputStream", "(I)V", (void*)android_os_MessageQueue_unregisterInputStream }, +}; + +int register_android_os_MessageQueue(JNIEnv* env) { + jclass clazz; + + clazz = env->FindClass(kMessageQueuePathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.MessageQueue"); + gMessageQueueOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gMessageQueueOffsets.mObject = env->GetFieldID(clazz, "mObject", "I"); + assert(gMessageQueueOffsets.mObject); + + clazz = env->FindClass(kMessagePathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Message"); + gMessageOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gMessageOffsets.mObtain = env->GetStaticMethodID(clazz, "obtain", + "(Landroid/os/Handler;ILjava/lang/Object;)Landroid/os/Message;"); + assert(gMessageOffsets.mObtain); + + clazz = env->FindClass(kKeyEventPathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.view.KeyEvent"); + gKeyEventOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gKeyEventOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(JJIIIIIII)V"); + assert(gKeyEventOffsets.mConstructor); + + return AndroidRuntime::registerNativeMethods(env, kMessageQueuePathName, + gMessageQueueMethods, NELEM(gMessageQueueMethods)); +} + + +}; // end of namespace android diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp index cf53a06..8c795af 100644 --- a/core/jni/android_server_BluetoothA2dpService.cpp +++ b/core/jni/android_server_BluetoothA2dpService.cpp @@ -256,10 +256,12 @@ DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) { parse_property_change(env, msg, (Properties *)&sink_properties, sizeof(sink_properties) / sizeof(Properties)); const char *c_path = dbus_message_get_path(msg); + jstring path = env->NewStringUTF(c_path); env->CallVoidMethod(nat->me, method_onSinkPropertyChanged, - env->NewStringUTF(c_path), + path, str_array); + env->DeleteLocalRef(path); result = DBUS_HANDLER_RESULT_HANDLED; return result; } else { @@ -292,10 +294,13 @@ void onConnectSinkResult(DBusMessage *msg, void *user, void *n) { result = JNI_FALSE; } LOGV("... Device Path = %s, result = %d", path, result); + + jstring jPath = env->NewStringUTF(path); env->CallVoidMethod(nat->me, method_onConnectSinkResult, - env->NewStringUTF(path), + jPath, result); + env->DeleteLocalRef(jPath); free(user); } diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 259cc01..01b6711 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -1061,6 +1061,8 @@ void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) { DBusError err; dbus_error_init(&err); JNIEnv *env; + jstring addr; + nat->vm->GetEnv((void**)&env, nat->envVer); LOGV("... address = %s", address); @@ -1109,10 +1111,12 @@ void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) { } } + addr = env->NewStringUTF(address); env->CallVoidMethod(nat->me, method_onCreatePairedDeviceResult, - env->NewStringUTF(address), + addr, result); + env->DeleteLocalRef(addr); done: dbus_error_free(&err); free(user); @@ -1139,10 +1143,12 @@ void onCreateDeviceResult(DBusMessage *msg, void *user, void *n) { } LOG_AND_FREE_DBUS_ERROR(&err); } + jstring addr = env->NewStringUTF(address); env->CallVoidMethod(nat->me, method_onCreateDeviceResult, - env->NewStringUTF(address), + addr, result); + env->DeleteLocalRef(addr); free(user); } @@ -1163,10 +1169,12 @@ void onDiscoverServicesResult(DBusMessage *msg, void *user, void *n) { LOG_AND_FREE_DBUS_ERROR(&err); result = JNI_FALSE; } + jstring jPath = env->NewStringUTF(path); env->CallVoidMethod(nat->me, method_onDiscoverServicesResult, - env->NewStringUTF(path), + jPath, result); + env->DeleteLocalRef(jPath); free(user); } @@ -1194,10 +1202,12 @@ void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) { } done: + jstring addr = env->NewStringUTF(address); env->CallVoidMethod(nat->me, method_onGetDeviceServiceChannelResult, - env->NewStringUTF(address), + addr, channel); + env->DeleteLocalRef(addr); free(user); } #endif diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index a2b7cc4..b7d0c67 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1504,8 +1504,7 @@ static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* } jobjectArray array = env->NewObjectArray(N, cls, NULL); - if (array == NULL) { - doThrow(env, "java/lang/OutOfMemoryError"); + if (env->ExceptionCheck()) { res.unlockBag(startOfBag); return NULL; } @@ -1533,15 +1532,23 @@ static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* } else { const char16_t* str16 = pool->stringAt(value.data, &strLen); str = env->NewString(str16, strLen); - if (str == NULL) { - doThrow(env, "java/lang/OutOfMemoryError"); - res.unlockBag(startOfBag); - return NULL; - } + } + + // If one of our NewString{UTF} calls failed due to memory, an + // exception will be pending. + if (env->ExceptionCheck()) { + res.unlockBag(startOfBag); + return NULL; } } env->SetObjectArrayElement(array, i, str); + + // If we have a large amount of strings in our array, we might + // overflow the local reference table of the VM. + if (str != NULL) { + env->DeleteLocalRef(str); + } } res.unlockBag(startOfBag); return array; diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 788374b..06fa84b 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -14,11 +14,14 @@ * limitations under the License. */ +#define LOG_TAG "Surface" + #include <stdio.h> #include "android_util_Binder.h" #include <surfaceflinger/SurfaceComposerClient.h> +#include <surfaceflinger/Surface.h> #include <ui/Region.h> #include <ui/Rect.h> @@ -225,8 +228,9 @@ static void Surface_initParcel(JNIEnv* env, jobject clazz, jobject argParcel) doThrow(env, "java/lang/NullPointerException", NULL); return; } - sp<Surface> rhs = new Surface(*parcel); - setSurface(env, clazz, rhs); + + sp<Surface> sur(Surface::readFromParcel(*parcel, 0)); + setSurface(env, clazz, sur); } static jint Surface_getIdentity(JNIEnv* env, jobject clazz) @@ -332,7 +336,7 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect) SkRegion clipReg; if (dirtyRegion.isRect()) { // very common case - const Rect& b(dirtyRegion.getBounds()); + const Rect b(dirtyRegion.getBounds()); clipReg.setRect(b.left, b.top, b.right, b.bottom); } else { size_t count; @@ -585,7 +589,7 @@ static void Surface_copyFrom( * a Surface and is necessary for returning the Surface reference to * the caller. At this point, we should only have a SurfaceControl. */ - + const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz); const sp<SurfaceControl>& rhs = getSurfaceControl(env, other); if (!SurfaceControl::isSameSurface(surface, rhs)) { @@ -604,13 +608,9 @@ static void Surface_readFromParcel( return; } - const sp<Surface>& control(getSurface(env, clazz)); - sp<Surface> rhs = new Surface(*parcel); - if (!Surface::isSameSurface(control, rhs)) { - // we reassign the surface only if it's a different one - // otherwise we would loose our client-side state. - setSurface(env, clazz, rhs); - } + const sp<Surface>& curr(getSurface(env, clazz)); + sp<Surface> sur(Surface::readFromParcel(*parcel, curr)); + setSurface(env, clazz, sur); } static void Surface_writeToParcel( @@ -680,7 +680,7 @@ static JNINativeMethod gSurfaceMethods[] = { void nativeClassInit(JNIEnv* env, jclass clazz) { - so.surface = env->GetFieldID(clazz, "mSurface", "I"); + so.surface = env->GetFieldID(clazz, ANDROID_VIEW_SURFACE_JNI_ID, "I"); so.surfaceControl = env->GetFieldID(clazz, "mSurfaceControl", "I"); so.saveCount = env->GetFieldID(clazz, "mSaveCount", "I"); so.canvas = env->GetFieldID(clazz, "mCanvas", "Landroid/graphics/Canvas;"); diff --git a/core/jni/android_view_ViewRoot.cpp b/core/jni/android_view_ViewRoot.cpp index 9d62d89..70ad8c5 100644 --- a/core/jni/android_view_ViewRoot.cpp +++ b/core/jni/android_view_ViewRoot.cpp @@ -16,6 +16,7 @@ #include <stdio.h> #include <assert.h> +#include <sys/socket.h> #include <core/SkCanvas.h> #include <core/SkDevice.h> @@ -24,6 +25,7 @@ #include "GraphicsJNI.h" #include "jni.h" +#include <nativehelper/JNIHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> @@ -78,6 +80,39 @@ static void android_view_ViewRoot_abandonGlCaches(JNIEnv* env, jobject) { SkGLCanvas::AbandonAllTextures(); } +static jintArray android_view_ViewRoot_makeInputChannel(JNIEnv* env, jobject) { + int fd[2]; + jint* arrayData = NULL; + + // Create the pipe + int err = socketpair(AF_LOCAL, SOCK_STREAM, 0, fd); + if (err != 0) { + fprintf(stderr, "socketpair() failed: %d\n", errno); + doThrow(env, "java/lang/RuntimeException", "Unable to create pipe"); + return NULL; + } + + // Set up the return array + jintArray array = env->NewIntArray(2); + if (env->ExceptionCheck()) { + fprintf(stderr, "Exception allocating fd array"); + goto bail; + } + + arrayData = env->GetIntArrayElements(array, 0); + arrayData[0] = fd[0]; + arrayData[1] = fd[1]; + env->ReleaseIntArrayElements(array, arrayData, 0); + + return array; + +bail: + env->DeleteLocalRef(array); + close(fd[0]); + close(fd[1]); + return NULL; +} + // ---------------------------------------------------------------------------- const char* const kClassPathName = "android/view/ViewRoot"; @@ -86,7 +121,9 @@ static JNINativeMethod gMethods[] = { { "nativeShowFPS", "(Landroid/graphics/Canvas;I)V", (void*)android_view_ViewRoot_showFPS }, { "nativeAbandonGlCaches", "()V", - (void*)android_view_ViewRoot_abandonGlCaches } + (void*)android_view_ViewRoot_abandonGlCaches }, + { "makeInputChannel", "()[I", + (void*)android_view_ViewRoot_makeInputChannel } }; int register_android_view_ViewRoot(JNIEnv* env) { diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp index 6a8c4b9..01a1504 100644 --- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp @@ -96,7 +96,7 @@ static void nativeClassInit(JNIEnv *_env, jclass eglImplClass) gConfig_EGLConfigFieldID = _env->GetFieldID(gConfig_class, "mEGLConfig", "I"); jclass surface_class = _env->FindClass("android/view/Surface"); - gSurface_SurfaceFieldID = _env->GetFieldID(surface_class, "mSurface", "I"); + gSurface_SurfaceFieldID = _env->GetFieldID(surface_class, ANDROID_VIEW_SURFACE_JNI_ID, "I"); jclass bitmap_class = _env->FindClass("android/graphics/Bitmap"); gBitmap_NativeBitmapFieldID = _env->GetFieldID(bitmap_class, "mNativeBitmap", "I"); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 2a2208f..aff9453 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -908,6 +908,13 @@ android:description="@string/permdesc_statusBar" android:protectionLevel="signatureOrSystem" /> + <!-- Allows an application to be the status bar. Currently used only by SystemUI.apk + @hide --> + <permission android:name="android.permission.STATUS_BAR_SERVICE" + android:label="@string/permlab_statusBarService" + android:description="@string/permdesc_statusBarService" + android:protectionLevel="signature" /> + <!-- Allows an application to force a BACK operation on whatever is the top activity. --> <permission android:name="android.permission.FORCE_BACK" @@ -1250,6 +1257,12 @@ <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> + <activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity" + android:theme="@style/Theme.Dialog" + android:label="@string/heavy_weight_switcher_title" + android:finishOnCloseSystemDialogs="true" + android:excludeFromRecents="true"> + </activity> <activity android:name="com.android.internal.app.DisableCarModeActivity" android:theme="@style/Theme.NoDisplay" android:excludeFromRecents="true"> @@ -1263,9 +1276,6 @@ <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> - <activity android:name="com.android.server.status.UsbStorageActivity" - android:excludeFromRecents="true"> - </activity> <activity android:name="com.android.internal.app.ExternalMediaFormatActivity" android:theme="@style/Theme.Dialog.Alert" android:excludeFromRecents="true"> diff --git a/core/res/res/drawable-hdpi/battery_charge_fill_empty.9.png b/core/res/res/drawable-hdpi/battery_charge_fill_empty.9.png Binary files differdeleted file mode 100644 index c4e70a8..0000000 --- a/core/res/res/drawable-hdpi/battery_charge_fill_empty.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/battery_charge_fill_full.9.png b/core/res/res/drawable-hdpi/battery_charge_fill_full.9.png Binary files differdeleted file mode 100644 index ac66f5a..0000000 --- a/core/res/res/drawable-hdpi/battery_charge_fill_full.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/battery_charge_fill_warning.9.png b/core/res/res/drawable-hdpi/battery_charge_fill_warning.9.png Binary files differdeleted file mode 100644 index 32d99c6..0000000 --- a/core/res/res/drawable-hdpi/battery_charge_fill_warning.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/battery_low_battery.png b/core/res/res/drawable-hdpi/battery_low_battery.png Binary files differdeleted file mode 100644 index d894f7b..0000000 --- a/core/res/res/drawable-hdpi/battery_low_battery.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_notify_alarm.png b/core/res/res/drawable-hdpi/stat_notify_alarm.png Binary files differdeleted file mode 100644 index 89daee1..0000000 --- a/core/res/res/drawable-hdpi/stat_notify_alarm.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png Binary files differindex 96dc085..7a8b78f 100644 --- a/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png +++ b/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png diff --git a/core/res/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png b/core/res/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png Binary files differdeleted file mode 100644 index 1e4bbf5..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_1x.png b/core/res/res/drawable-hdpi/stat_sys_data_connected_1x.png Binary files differdeleted file mode 100644 index 12d4ac4..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_connected_1x.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_3g.png b/core/res/res/drawable-hdpi/stat_sys_data_connected_3g.png Binary files differdeleted file mode 100644 index 4f1f377..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_connected_3g.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_e.png b/core/res/res/drawable-hdpi/stat_sys_data_connected_e.png Binary files differdeleted file mode 100644 index c65f56a..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_connected_e.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_g.png b/core/res/res/drawable-hdpi/stat_sys_data_connected_g.png Binary files differdeleted file mode 100644 index 4aca0c7..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_connected_g.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_h.png b/core/res/res/drawable-hdpi/stat_sys_data_connected_h.png Binary files differdeleted file mode 100755 index 24e07ab..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_connected_h.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_1x.png b/core/res/res/drawable-hdpi/stat_sys_data_in_1x.png Binary files differdeleted file mode 100644 index 511e22a..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_in_1x.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_3g.png b/core/res/res/drawable-hdpi/stat_sys_data_in_3g.png Binary files differdeleted file mode 100644 index 64f8087..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_in_3g.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_e.png b/core/res/res/drawable-hdpi/stat_sys_data_in_e.png Binary files differdeleted file mode 100644 index 90aaf71..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_in_e.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_g.png b/core/res/res/drawable-hdpi/stat_sys_data_in_g.png Binary files differdeleted file mode 100644 index c21387e..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_in_g.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_h.png b/core/res/res/drawable-hdpi/stat_sys_data_in_h.png Binary files differdeleted file mode 100755 index f2f6daa..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_in_h.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_1x.png b/core/res/res/drawable-hdpi/stat_sys_data_inandout_1x.png Binary files differdeleted file mode 100644 index 19ea80c..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_inandout_1x.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_3g.png b/core/res/res/drawable-hdpi/stat_sys_data_inandout_3g.png Binary files differdeleted file mode 100644 index ebfa6fb..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_inandout_3g.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_e.png b/core/res/res/drawable-hdpi/stat_sys_data_inandout_e.png Binary files differdeleted file mode 100644 index 7ccd7de..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_inandout_e.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_g.png b/core/res/res/drawable-hdpi/stat_sys_data_inandout_g.png Binary files differdeleted file mode 100644 index c614d0d..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_inandout_g.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_h.png b/core/res/res/drawable-hdpi/stat_sys_data_inandout_h.png Binary files differdeleted file mode 100755 index 5d6ef05..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_inandout_h.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_1x.png b/core/res/res/drawable-hdpi/stat_sys_data_out_1x.png Binary files differdeleted file mode 100644 index adf5f95..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_out_1x.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_3g.png b/core/res/res/drawable-hdpi/stat_sys_data_out_3g.png Binary files differdeleted file mode 100644 index 9936f2a..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_out_3g.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_e.png b/core/res/res/drawable-hdpi/stat_sys_data_out_e.png Binary files differdeleted file mode 100644 index 904c565..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_out_e.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_g.png b/core/res/res/drawable-hdpi/stat_sys_data_out_g.png Binary files differdeleted file mode 100644 index 1261c15..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_out_g.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_h.png b/core/res/res/drawable-hdpi/stat_sys_data_out_h.png Binary files differdeleted file mode 100755 index 5e3122d..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_data_out_h.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_gps_acquiring.png b/core/res/res/drawable-hdpi/stat_sys_gps_acquiring.png Binary files differdeleted file mode 100644 index 9003d67..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_gps_acquiring.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_no_sim.png b/core/res/res/drawable-hdpi/stat_sys_no_sim.png Binary files differdeleted file mode 100644 index 157491e..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_no_sim.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_0.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_0.png Binary files differdeleted file mode 100644 index 95ba181..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_r_signal_0.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_1.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_1.png Binary files differdeleted file mode 100644 index adf668d..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_r_signal_1.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_2.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_2.png Binary files differdeleted file mode 100644 index 7bf6b51..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_r_signal_2.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_3.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_3.png Binary files differdeleted file mode 100644 index 78738ac..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_r_signal_3.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_4.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_4.png Binary files differdeleted file mode 100644 index ac88143..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_r_signal_4.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_ringer_silent.png b/core/res/res/drawable-hdpi/stat_sys_ringer_silent.png Binary files differdeleted file mode 100644 index bdd37e1..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_ringer_silent.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_ringer_vibrate.png b/core/res/res/drawable-hdpi/stat_sys_ringer_vibrate.png Binary files differdeleted file mode 100644 index 21c1c08..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_ringer_vibrate.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_0.png b/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_0.png Binary files differdeleted file mode 100644 index adde938..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_0.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim0.png b/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim0.png Binary files differdeleted file mode 100644 index 245677b..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim0.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim1.png b/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim1.png Binary files differdeleted file mode 100644 index adde938..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim1.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_0.png b/core/res/res/drawable-hdpi/stat_sys_signal_0.png Binary files differdeleted file mode 100644 index 3e317dd..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_signal_0.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_1.png b/core/res/res/drawable-hdpi/stat_sys_signal_1.png Binary files differdeleted file mode 100644 index 72329f8..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_signal_1.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_2.png b/core/res/res/drawable-hdpi/stat_sys_signal_2.png Binary files differdeleted file mode 100644 index 558c49c..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_signal_2.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_3.png b/core/res/res/drawable-hdpi/stat_sys_signal_3.png Binary files differdeleted file mode 100644 index 6440bdd..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_signal_3.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_4.png b/core/res/res/drawable-hdpi/stat_sys_signal_4.png Binary files differdeleted file mode 100644 index fe20423..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_signal_4.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_flightmode.png b/core/res/res/drawable-hdpi/stat_sys_signal_flightmode.png Binary files differdeleted file mode 100644 index 7a419f1..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_signal_flightmode.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_null.png b/core/res/res/drawable-hdpi/stat_sys_signal_null.png Binary files differdeleted file mode 100644 index 1adc05a..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_signal_null.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_tty_mode.png b/core/res/res/drawable-hdpi/stat_sys_tty_mode.png Binary files differdeleted file mode 100644 index 4e161c6..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_tty_mode.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_0.png b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_0.png Binary files differdeleted file mode 100644 index 55a2ad8..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_0.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_1.png b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_1.png Binary files differdeleted file mode 100644 index d16b3e8..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_1.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_2.png b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_2.png Binary files differdeleted file mode 100644 index 2511083..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_2.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_3.png b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_3.png Binary files differdeleted file mode 100644 index e0799a5..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_3.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_4.png b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_4.png Binary files differdeleted file mode 100644 index 2385c3a..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_4.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/status_bar_background.png b/core/res/res/drawable-hdpi/status_bar_background.png Binary files differindex e6a865a..3d00cd0 100644 --- a/core/res/res/drawable-hdpi/status_bar_background.png +++ b/core/res/res/drawable-hdpi/status_bar_background.png diff --git a/core/res/res/drawable-hdpi/status_bar_close_on.9.png b/core/res/res/drawable-hdpi/status_bar_close_on.9.png Binary files differindex 5acf638..f313ffb 100644 --- a/core/res/res/drawable-hdpi/status_bar_close_on.9.png +++ b/core/res/res/drawable-hdpi/status_bar_close_on.9.png diff --git a/core/res/res/drawable-hdpi/status_bar_header_background.9.png b/core/res/res/drawable-hdpi/status_bar_header_background.9.png Binary files differindex be36ff2..37b5fef 100644 --- a/core/res/res/drawable-hdpi/status_bar_header_background.9.png +++ b/core/res/res/drawable-hdpi/status_bar_header_background.9.png diff --git a/core/res/res/drawable-hdpi/statusbar_background.9.png b/core/res/res/drawable-hdpi/statusbar_background.9.png Binary files differindex dcca695..a4be298 100644 --- a/core/res/res/drawable-hdpi/statusbar_background.9.png +++ b/core/res/res/drawable-hdpi/statusbar_background.9.png diff --git a/core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png b/core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png Binary files differdeleted file mode 100644 index 9ed20ba..0000000 --- a/core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/battery_charge_fill_full.9.png b/core/res/res/drawable-mdpi/battery_charge_fill_full.9.png Binary files differdeleted file mode 100644 index 8e6aaca..0000000 --- a/core/res/res/drawable-mdpi/battery_charge_fill_full.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/battery_charge_fill_warning.9.png b/core/res/res/drawable-mdpi/battery_charge_fill_warning.9.png Binary files differdeleted file mode 100644 index d3287db..0000000 --- a/core/res/res/drawable-mdpi/battery_charge_fill_warning.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/battery_low_battery.png b/core/res/res/drawable-mdpi/battery_low_battery.png Binary files differdeleted file mode 100644 index 60bbe6c..0000000 --- a/core/res/res/drawable-mdpi/battery_low_battery.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_notify_alarm.png b/core/res/res/drawable-mdpi/stat_notify_alarm.png Binary files differdeleted file mode 100644 index 1b01b85..0000000 --- a/core/res/res/drawable-mdpi/stat_notify_alarm.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png b/core/res/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png Binary files differdeleted file mode 100755 index f09b83b..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_connected_1x.png b/core/res/res/drawable-mdpi/stat_sys_data_connected_1x.png Binary files differdeleted file mode 100644 index 130724f..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_connected_1x.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_connected_3g.png b/core/res/res/drawable-mdpi/stat_sys_data_connected_3g.png Binary files differdeleted file mode 100644 index a109280..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_connected_3g.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_connected_e.png b/core/res/res/drawable-mdpi/stat_sys_data_connected_e.png Binary files differdeleted file mode 100644 index c552644..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_connected_e.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_connected_g.png b/core/res/res/drawable-mdpi/stat_sys_data_connected_g.png Binary files differdeleted file mode 100644 index f7edb49..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_connected_g.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_connected_h.png b/core/res/res/drawable-mdpi/stat_sys_data_connected_h.png Binary files differdeleted file mode 100644 index 7d5413a..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_connected_h.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_in_1x.png b/core/res/res/drawable-mdpi/stat_sys_data_in_1x.png Binary files differdeleted file mode 100644 index 3155e632..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_in_1x.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_in_3g.png b/core/res/res/drawable-mdpi/stat_sys_data_in_3g.png Binary files differdeleted file mode 100644 index 01b003c..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_in_3g.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_in_e.png b/core/res/res/drawable-mdpi/stat_sys_data_in_e.png Binary files differdeleted file mode 100644 index bffa0eb..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_in_e.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_in_g.png b/core/res/res/drawable-mdpi/stat_sys_data_in_g.png Binary files differdeleted file mode 100644 index 8884b48..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_in_g.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_in_h.png b/core/res/res/drawable-mdpi/stat_sys_data_in_h.png Binary files differdeleted file mode 100644 index 695b80c..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_in_h.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_inandout_1x.png b/core/res/res/drawable-mdpi/stat_sys_data_inandout_1x.png Binary files differdeleted file mode 100644 index 1017e3b..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_inandout_1x.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_inandout_3g.png b/core/res/res/drawable-mdpi/stat_sys_data_inandout_3g.png Binary files differdeleted file mode 100644 index 3651300..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_inandout_3g.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_inandout_e.png b/core/res/res/drawable-mdpi/stat_sys_data_inandout_e.png Binary files differdeleted file mode 100644 index 99533e0..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_inandout_e.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_inandout_g.png b/core/res/res/drawable-mdpi/stat_sys_data_inandout_g.png Binary files differdeleted file mode 100644 index f4e5a12..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_inandout_g.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_inandout_h.png b/core/res/res/drawable-mdpi/stat_sys_data_inandout_h.png Binary files differdeleted file mode 100644 index 467acd1..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_inandout_h.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_out_1x.png b/core/res/res/drawable-mdpi/stat_sys_data_out_1x.png Binary files differdeleted file mode 100644 index 5418791..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_out_1x.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_out_3g.png b/core/res/res/drawable-mdpi/stat_sys_data_out_3g.png Binary files differdeleted file mode 100644 index f7f0f89..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_out_3g.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_out_e.png b/core/res/res/drawable-mdpi/stat_sys_data_out_e.png Binary files differdeleted file mode 100644 index c915426..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_out_e.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_out_g.png b/core/res/res/drawable-mdpi/stat_sys_data_out_g.png Binary files differdeleted file mode 100644 index 5d36035..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_out_g.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_data_out_h.png b/core/res/res/drawable-mdpi/stat_sys_data_out_h.png Binary files differdeleted file mode 100644 index da50305..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_data_out_h.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_gps_acquiring.png b/core/res/res/drawable-mdpi/stat_sys_gps_acquiring.png Binary files differdeleted file mode 100644 index 31bc94e..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_gps_acquiring.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_no_sim.png b/core/res/res/drawable-mdpi/stat_sys_no_sim.png Binary files differdeleted file mode 100644 index 2134d49..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_no_sim.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_0.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_0.png Binary files differdeleted file mode 100644 index bfbf18e..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_r_signal_0.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_1.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_1.png Binary files differdeleted file mode 100644 index 896ba4d..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_r_signal_1.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_2.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_2.png Binary files differdeleted file mode 100644 index af79eff..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_r_signal_2.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_3.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_3.png Binary files differdeleted file mode 100644 index 92c09c8..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_r_signal_3.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_4.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_4.png Binary files differdeleted file mode 100644 index f04fb11..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_r_signal_4.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_ringer_silent.png b/core/res/res/drawable-mdpi/stat_sys_ringer_silent.png Binary files differdeleted file mode 100644 index d62f32d..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_ringer_silent.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_ringer_vibrate.png b/core/res/res/drawable-mdpi/stat_sys_ringer_vibrate.png Binary files differdeleted file mode 100644 index 665ca38..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_ringer_vibrate.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_0.png b/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_0.png Binary files differdeleted file mode 100755 index c61cce7..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_0.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim0.png b/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim0.png Binary files differdeleted file mode 100755 index d62502d..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim0.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim1.png b/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim1.png Binary files differdeleted file mode 100755 index c61cce7..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim1.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_0.png b/core/res/res/drawable-mdpi/stat_sys_signal_0.png Binary files differdeleted file mode 100644 index cb7b7b3..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_signal_0.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_1.png b/core/res/res/drawable-mdpi/stat_sys_signal_1.png Binary files differdeleted file mode 100644 index 5376e92..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_signal_1.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_2.png b/core/res/res/drawable-mdpi/stat_sys_signal_2.png Binary files differdeleted file mode 100644 index fd54363..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_signal_2.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_3.png b/core/res/res/drawable-mdpi/stat_sys_signal_3.png Binary files differdeleted file mode 100644 index 6c4873a..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_signal_3.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_4.png b/core/res/res/drawable-mdpi/stat_sys_signal_4.png Binary files differdeleted file mode 100644 index a3320cb..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_signal_4.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_flightmode.png b/core/res/res/drawable-mdpi/stat_sys_signal_flightmode.png Binary files differdeleted file mode 100755 index 2f4fd4f..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_signal_flightmode.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_null.png b/core/res/res/drawable-mdpi/stat_sys_signal_null.png Binary files differdeleted file mode 100644 index 5aa23f6..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_signal_null.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_tty_mode.png b/core/res/res/drawable-mdpi/stat_sys_tty_mode.png Binary files differdeleted file mode 100644 index ed157a8..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_tty_mode.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_0.png b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_0.png Binary files differdeleted file mode 100644 index 8ee3421..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_0.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_1.png b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_1.png Binary files differdeleted file mode 100644 index 184fa36..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_1.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_2.png b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_2.png Binary files differdeleted file mode 100644 index 79935bb..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_2.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_3.png b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_3.png Binary files differdeleted file mode 100644 index d2099e6..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_3.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_4.png b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_4.png Binary files differdeleted file mode 100644 index 2062aad..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_wifi_signal_4.png +++ /dev/null diff --git a/core/res/res/drawable/battery_charge_fill.xml b/core/res/res/drawable/battery_charge_fill.xml deleted file mode 100644 index 7f65733..0000000 --- a/core/res/res/drawable/battery_charge_fill.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2008 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<level-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:maxLevel="29" android:drawable="@android:drawable/battery_charge_fill_empty" /> - <item android:maxLevel="49" android:drawable="@android:drawable/battery_charge_fill_warning" /> - <item android:maxLevel="100" android:drawable="@android:drawable/battery_charge_fill_full" /> -</level-list> - diff --git a/core/res/res/drawable/stat_sys_gps_acquiring_anim.xml b/core/res/res/drawable/stat_sys_gps_acquiring_anim.xml deleted file mode 100644 index 954c19c..0000000 --- a/core/res/res/drawable/stat_sys_gps_acquiring_anim.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 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. -*/ ---> -<animation-list - xmlns:android="http://schemas.android.com/apk/res/android" - android:oneshot="false"> - <item android:drawable="@drawable/stat_sys_gps_acquiring" android:duration="500" /> - <item android:drawable="@drawable/stat_sys_gps_on" android:duration="500" /> -</animation-list> diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_flash.xml b/core/res/res/drawable/stat_sys_roaming_cdma_flash.xml deleted file mode 100644 index 07dc446..0000000 --- a/core/res/res/drawable/stat_sys_roaming_cdma_flash.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/stat_sys_battery.xml -** -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** 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. -*/ ---> -<animation-list - xmlns:android="http://schemas.android.com/apk/res/android" - android:oneshot="false"> - <item android:drawable="@drawable/stat_sys_roaming_cdma_flash_anim0" android:duration="800" /> - <item android:drawable="@drawable/stat_sys_roaming_cdma_flash_anim1" android:duration="1200" /> -</animation-list> diff --git a/core/res/res/drawable/status_bar_item_background.xml b/core/res/res/drawable/status_bar_item_background.xml index 088389b..425a502 100644 --- a/core/res/res/drawable/status_bar_item_background.xml +++ b/core/res/res/drawable/status_bar_item_background.xml @@ -16,7 +16,7 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" - android:drawable="@drawable/status_bar_item_background_pressed" /> + android:drawable="@drawable/status_bar_item_background_pressed" /> <item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/status_bar_item_background_focus" /> <item android:drawable="@drawable/status_bar_item_background_normal" /> diff --git a/core/res/res/drawable/status_icon_background.xml b/core/res/res/drawable/status_icon_background.xml deleted file mode 100644 index 9846165..0000000 --- a/core/res/res/drawable/status_icon_background.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/status_icon_background.xml -** -** Copyright 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. -*/ ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_selected="true" android:drawable="@drawable/icon_highlight_rectangle" /> - <item android:drawable="@color/transparent" /> -</selector> diff --git a/core/res/res/layout/battery_low.xml b/core/res/res/layout/battery_low.xml deleted file mode 100644 index 3b62fb0..0000000 --- a/core/res/res/layout/battery_low.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/layout/keyguard.xml -** -** Copyright 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. -*/ ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/padding" - android:orientation="vertical" - android:gravity="center" - > - - <TextView android:id="@+id/subtitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textSize="18dp" - android:paddingLeft="19dp" - android:textColor="#ffffffff" - android:gravity="left" - android:text="@string/battery_low_subtitle" - /> - - <TextView android:id="@+id/level_percent" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textSize="18dp" - android:textColor="#ffffffff" - android:gravity="left" - android:paddingBottom="10px" - android:paddingLeft="19dp" - /> - - <ImageView android:id="@+id/image" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingBottom="15px" - android:src="@drawable/battery_low_battery" - android:paddingTop="10px" - /> - -</LinearLayout> - - diff --git a/core/res/res/layout/battery_status.xml b/core/res/res/layout/battery_status.xml deleted file mode 100644 index 7cfec05..0000000 --- a/core/res/res/layout/battery_status.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2008 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/frame" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical" - android:gravity="center_horizontal" - > - - <FrameLayout - android:layout_width="141px" - android:layout_height="184px" - android:background="@drawable/battery_charge_background" - android:paddingTop="25px" - android:paddingLeft="1px" - > - - <LinearLayout - android:id="@+id/meter" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - > - - <ImageView - android:layout_width="match_parent" - android:layout_height="15dip" - /> - <ImageView - android:id="@+id/spacer" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - /> - <ImageView - android:id="@+id/level" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - /> - - </LinearLayout> - - <TextView android:id="@+id/level_percent" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:textStyle="bold" - android:textSize="48dp" - android:textColor="#ffffffff" - android:gravity="center" - /> - </FrameLayout> - - <TextView android:id="@+id/status" - android:paddingTop="35dp" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:textStyle="bold" - android:textSize="30dp" - android:textColor="#ffffffff" - android:gravity="center_horizontal" - android:text="@string/battery_status_charging" - /> - -</LinearLayout> - - diff --git a/core/res/res/layout/heavy_weight_switcher.xml b/core/res/res/layout/heavy_weight_switcher.xml new file mode 100644 index 0000000..9acf009 --- /dev/null +++ b/core/res/res/layout/heavy_weight_switcher.xml @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:padding="4dp" + android:gravity="center_horizontal" + android:layout_width="wrap_content" android:layout_height="wrap_content"> + + <TextView + android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_weight="0" + android:paddingBottom="8dp" + android:text="@string/heavy_weight_switcher_text"/> + + <ImageView android:layout_width="match_parent" + android:layout_height="wrap_content" + android:scaleType="fitXY" + android:src="?android:listDivider" /> + + <LinearLayout android:id="@+id/switch_old" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:orientation="horizontal" + android:background="@android:drawable/list_selector_background" + android:paddingRight="3dip" + android:paddingLeft="3dip" + android:paddingTop="5dip" + android:paddingBottom="14dip" + android:gravity="center_vertical" + android:focusable="true" > + + <ImageView android:id="@+id/old_app_icon" + android:layout_width="@android:dimen/app_icon_size" + android:layout_height="@android:dimen/app_icon_size" + android:layout_marginRight="11dip" + android:layout_gravity="center_vertical" + android:scaleType="fitCenter"/> + + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:duplicateParentState="true" > + <TextView android:id="@+id/old_app_action" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textStyle="bold" + android:singleLine="true" + android:layout_marginBottom="2dip" + android:duplicateParentState="true" /> + <TextView android:id="@+id/old_app_description" + android:layout_marginTop="-4dip" + android:layout_gravity="center_vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceSmall" + android:duplicateParentState="true" /> + </LinearLayout> + </LinearLayout> + + <ImageView android:layout_width="match_parent" + android:layout_height="wrap_content" + android:scaleType="fitXY" + android:src="?android:listDivider" /> + + <LinearLayout android:id="@+id/switch_new" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:orientation="horizontal" + android:background="@android:drawable/list_selector_background" + android:paddingRight="3dip" + android:paddingLeft="3dip" + android:paddingTop="5dip" + android:paddingBottom="8dip" + android:gravity="center_vertical" + android:focusable="true" > + + <ImageView android:id="@+id/new_app_icon" + android:layout_width="@android:dimen/app_icon_size" + android:layout_height="@android:dimen/app_icon_size" + android:layout_marginRight="11dip" + android:layout_gravity="center_vertical" + android:scaleType="fitCenter"/> + + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:duplicateParentState="true" > + <TextView android:id="@+id/new_app_action" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textStyle="bold" + android:singleLine="true" + android:layout_marginBottom="2dip" + android:duplicateParentState="true" /> + <TextView android:id="@+id/new_app_description" + android:layout_marginTop="-4dip" + android:layout_gravity="center_vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceSmall" + android:duplicateParentState="true" /> + </LinearLayout> + </LinearLayout> + + <ImageView android:layout_width="match_parent" + android:layout_height="wrap_content" + android:scaleType="fitXY" + android:src="?android:listDivider" /> + + <TextView android:id="@+id/cancel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:textAppearance="?android:attr/textAppearanceMedium" + android:background="@android:drawable/list_selector_background" + android:paddingRight="6dip" + android:paddingLeft="6dip" + android:paddingTop="5dip" + android:paddingBottom="8dip" + android:textStyle="bold" + android:singleLine="true" + android:gravity="center" + android:focusable="true" + android:text="@string/cancel" /> + +</LinearLayout> diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml index 200a1b2..6edbdf9 100644 --- a/core/res/res/layout/keyguard_screen_tab_unlock.xml +++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml @@ -41,7 +41,6 @@ android:ellipsize="marquee" android:gravity="right|bottom" android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="22sp" /> <!-- "emergency calls only" shown when sim is missing or PUKd --> @@ -65,7 +64,7 @@ android:layout_below="@id/carrier" android:layout_marginTop="52dip" android:layout_marginLeft="20dip" - android:paddingBottom="8dip" + android:layout_marginBottom="8dip" > <TextView android:id="@+id/timeDisplay" diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml index 23505c2..e66b492 100644 --- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml +++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml @@ -46,7 +46,6 @@ android:ellipsize="marquee" android:gravity="right|bottom" android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="22sp" /> <!-- "emergency calls only" shown when sim is missing or PUKd --> @@ -66,7 +65,7 @@ android:layout_height="wrap_content" android:layout_below="@id/carrier" android:layout_marginTop="56dip" - android:paddingBottom="8dip" + android:layout_marginBottom="8dip" > <TextView android:id="@+id/timeDisplay" diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml index b5cd442..c1b406f 100644 --- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml +++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml @@ -58,19 +58,18 @@ android:ellipsize="marquee" android:gravity="right|bottom" /> - <com.android.internal.widget.DigitalClock android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_marginTop="8dip" - android:paddingBottom="8dip" > <TextView android:id="@+id/timeDisplay" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:gravity="bottom" android:singleLine="true" android:ellipsize="none" android:textSize="72sp" @@ -85,9 +84,8 @@ <TextView android:id="@+id/am_pm" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/timeDisplay" - android:layout_alignBaseline="@id/timeDisplay" + android:layout_height="match_parent" + android:gravity="bottom" android:singleLine="true" android:ellipsize="none" android:textSize="22sp" diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml index 9ac0a47..74a0eee 100644 --- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml +++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml @@ -55,12 +55,12 @@ android:layout_alignParentTop="true" android:layout_marginTop="15dip" android:layout_marginLeft="20dip" - android:paddingBottom="8dip" > <TextView android:id="@+id/timeDisplay" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:gravity="bottom" android:singleLine="true" android:ellipsize="none" android:textSize="56sp" @@ -74,9 +74,8 @@ <TextView android:id="@+id/am_pm" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/timeDisplay" - android:layout_alignBaseline="@id/timeDisplay" + android:layout_height="match_parent" + android:gravity="bottom" android:singleLine="true" android:ellipsize="none" android:textSize="18sp" diff --git a/core/res/res/layout/status_bar.xml b/core/res/res/layout/status_bar.xml deleted file mode 100644 index e8d8866..0000000 --- a/core/res/res/layout/status_bar.xml +++ /dev/null @@ -1,103 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* apps/common/assets/default/default/skins/StatusBar.xml -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- android:background="@drawable/status_bar_closed_default_background" --> -<com.android.server.status.StatusBarView xmlns:android="http://schemas.android.com/apk/res/android" - android:background="@drawable/statusbar_background" - android:orientation="vertical" - android:focusable="true" - android:descendantFocusability="afterDescendants" - > - - <LinearLayout android:id="@+id/icons" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal"> - - <com.android.server.status.IconMerger android:id="@+id/notificationIcons" - android:layout_width="0dip" - android:layout_weight="1" - android:layout_height="match_parent" - android:layout_alignParentLeft="true" - android:paddingLeft="6dip" - android:gravity="center_vertical" - android:orientation="horizontal"/> - - <LinearLayout android:id="@+id/statusIcons" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_alignParentRight="true" - android:paddingRight="6dip" - android:gravity="center_vertical" - android:orientation="horizontal"/> - </LinearLayout> - - <LinearLayout android:id="@+id/ticker" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingLeft="6dip" - android:animationCache="false" - android:orientation="horizontal" > - <ImageSwitcher android:id="@+id/tickerIcon" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginRight="8dip" - > - <com.android.server.status.AnimatedImageView - android:layout_width="25dip" - android:layout_height="25dip" - /> - <com.android.server.status.AnimatedImageView - android:layout_width="25dip" - android:layout_height="25dip" - /> - </ImageSwitcher> - <com.android.server.status.TickerView android:id="@+id/tickerText" - android:layout_width="0dip" - android:layout_weight="1" - android:layout_height="wrap_content" - android:paddingTop="2dip" - android:paddingRight="10dip"> - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - android:textColor="#ff000000" /> - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - android:textColor="#ff000000" /> - </com.android.server.status.TickerView> - </LinearLayout> - - <com.android.server.status.DateView android:id="@+id/date" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:singleLine="true" - android:textSize="16sp" - android:textStyle="bold" - android:gravity="center_vertical|left" - android:paddingLeft="6px" - android:paddingRight="6px" - android:textColor="?android:attr/textColorPrimaryInverse" - android:background="@drawable/statusbar_background" - /> -</com.android.server.status.StatusBarView> diff --git a/core/res/res/layout/status_bar_expanded.xml b/core/res/res/layout/status_bar_expanded.xml deleted file mode 100644 index 30138a7..0000000 --- a/core/res/res/layout/status_bar_expanded.xml +++ /dev/null @@ -1,144 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* apps/common/assets/default/default/skins/StatusBar.xml -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<com.android.server.status.ExpandedView xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:focusable="true" - android:descendantFocusability="afterDescendants" - > - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:paddingTop="3dp" - android:paddingBottom="5dp" - android:paddingRight="3dp" - android:background="@drawable/status_bar_header_background" - > - <LinearLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_marginTop="1dp" - android:layout_marginLeft="5dp" - android:layout_gravity="center_vertical" - android:paddingBottom="1dp" - android:orientation="vertical" - > - <TextView android:id="@+id/plmnLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:textAppearance="?android:attr/textAppearanceLarge" - android:textColor="?android:attr/textColorSecondaryInverse" - android:paddingLeft="4dp" - /> - <TextView android:id="@+id/spnLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:textAppearance="?android:attr/textAppearanceLarge" - android:textColor="?android:attr/textColorSecondaryInverse" - android:paddingLeft="4dp" - /> - </LinearLayout> - <TextView android:id="@+id/clear_all_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:layout_marginTop="4dp" - android:layout_marginBottom="1dp" - android:textSize="14sp" - android:textColor="#ff000000" - android:text="@string/status_bar_clear_all_button" - style="?android:attr/buttonStyle" - android:paddingLeft="15dp" - android:paddingRight="15dp" - android:background="@drawable/btn_default_small" - /> - </LinearLayout> - - <FrameLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_weight="1" - > - <ScrollView - android:id="@+id/scroll" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:fadingEdge="none" - > - <com.android.server.status.NotificationLinearLayout - android:id="@+id/notificationLinearLayout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - > - - <TextView android:id="@+id/noNotificationsTitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/title_bar_portrait" - android:paddingLeft="5dp" - android:textAppearance="@style/TextAppearance.StatusBarTitle" - android:text="@string/status_bar_no_notifications_title" - /> - - <TextView android:id="@+id/ongoingTitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/title_bar_portrait" - android:paddingLeft="5dp" - android:textAppearance="@style/TextAppearance.StatusBarTitle" - android:text="@string/status_bar_ongoing_events_title" - /> - <LinearLayout android:id="@+id/ongoingItems" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - /> - - <TextView android:id="@+id/latestTitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/title_bar_portrait" - android:paddingLeft="5dp" - android:textAppearance="@style/TextAppearance.StatusBarTitle" - android:text="@string/status_bar_latest_events_title" - /> - <LinearLayout android:id="@+id/latestItems" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - /> - </com.android.server.status.NotificationLinearLayout> - </ScrollView> - - <ImageView - android:layout_width="match_parent" - android:layout_height="match_parent" - android:src="@drawable/title_bar_shadow" - android:scaleType="fitXY" - /> - - </FrameLayout> -</com.android.server.status.ExpandedView> diff --git a/core/res/res/layout/status_bar_icon.xml b/core/res/res/layout/status_bar_icon.xml deleted file mode 100644 index 0536792..0000000 --- a/core/res/res/layout/status_bar_icon.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* apps/common/assets/default/default/skins/StatusBar.xml -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<!-- The icons are a fixed size so an app can't mess everything up with bogus images --> -<!-- TODO: the icons are hard coded to 25x25 pixels. Their size should come froem a theme --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="25dp" - android:layout_height="25dp" - > - - <com.android.server.status.AnimatedImageView android:id="@+id/image" - android:layout_width="match_parent" - android:layout_height="match_parent" - /> - - <TextView android:id="@+id/number" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="right|bottom" - android:layout_marginRight="1dp" - android:layout_marginBottom="1dp" - android:textSize="10sp" - android:textColor="#ffffffff" - android:background="@drawable/ic_notification_overlay" - android:gravity="center" - android:textStyle="bold" - /> - -</FrameLayout> diff --git a/core/res/res/layout/status_bar_latest_event.xml b/core/res/res/layout/status_bar_latest_event.xml deleted file mode 100644 index 59cc90d..0000000 --- a/core/res/res/layout/status_bar_latest_event.xml +++ /dev/null @@ -1,24 +0,0 @@ -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="65sp" - android:orientation="vertical" - > - - <com.android.server.status.LatestItemView android:id="@+id/content" - android:layout_width="match_parent" - android:layout_height="64sp" - android:background="@drawable/status_bar_item_background" - android:focusable="true" - android:clickable="true" - android:paddingRight="6sp" - > - </com.android.server.status.LatestItemView> - - <View - android:layout_width="match_parent" - android:layout_height="1sp" - android:background="@drawable/divider_horizontal_bright" - /> - -</LinearLayout> - diff --git a/core/res/res/layout/status_bar_latest_event_content.xml b/core/res/res/layout/status_bar_latest_event_content.xml index c3aa041..c64b90e 100644 --- a/core/res/res/layout/status_bar_latest_event_content.xml +++ b/core/res/res/layout/status_bar_latest_event_content.xml @@ -12,22 +12,22 @@ android:orientation="horizontal" android:paddingTop="3dp" > - <com.android.server.status.AnimatedImageView android:id="@+id/icon" + <!--com.android.server.status.AnimatedImageView android:id="@+id/icon" --> + <ImageView android:id="@+id/icon" android:layout_width="25dp" android:layout_height="25dp" android:scaleType="fitCenter" android:src="@drawable/arrow_down_float"/> <TextView android:id="@+id/title" + android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:singleLine="true" android:ellipsize="marquee" android:fadingEdge="horizontal" - android:textStyle="bold" - android:textSize="18sp" android:paddingLeft="4dp" - android:textColor="#ff000000" /> + /> </LinearLayout> <LinearLayout android:layout_width="match_parent" @@ -35,23 +35,22 @@ android:orientation="horizontal" > <TextView android:id="@+id/text" + android:textAppearance="@style/TextAppearance.StatusBar.EventContent" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" - android:textColor="#ff000000" android:singleLine="true" android:ellipsize="marquee" android:fadingEdge="horizontal" - android:textSize="14sp" android:paddingLeft="4dp" /> <android.widget.DateTimeView android:id="@+id/time" + android:textAppearance="@style/TextAppearance.StatusBar.EventContent" android:layout_marginLeft="4dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textSize="14sp" android:paddingRight="5dp" - android:textColor="#ff000000" /> + /> </LinearLayout> </LinearLayout> diff --git a/core/res/res/layout/status_bar_tracking.xml b/core/res/res/layout/status_bar_tracking.xml deleted file mode 100644 index c0a7a97..0000000 --- a/core/res/res/layout/status_bar_tracking.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2008 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<com.android.server.status.TrackingView xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:visibility="gone" - android:focusable="true" - android:descendantFocusability="afterDescendants" - android:paddingBottom="0px" - android:paddingLeft="0px" - android:paddingRight="0px" - > - - <com.android.server.status.TrackingPatternView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_weight="1" - /> - - <com.android.server.status.CloseDragHandle android:id="@+id/close" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - > - <ImageView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="bottom" - android:scaleType="fitXY" - android:src="@drawable/status_bar_close_on"/> - - </com.android.server.status.CloseDragHandle> - -</com.android.server.status.TrackingView> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 4672c0e..aeee8af 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -117,28 +117,4 @@ <item>3</item> </integer-array> - <!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the - icons in the status bar that are not notifications. --> - <string-array name="status_bar_icon_order"> - <item><xliff:g id="id">clock</xliff:g></item> - <item><xliff:g id="id">secure</xliff:g></item> - <item><xliff:g id="id">alarm_clock</xliff:g></item> - <item><xliff:g id="id">battery</xliff:g></item> - <item><xliff:g id="id">phone_signal</xliff:g></item> - <item><xliff:g id="id">phone_evdo_signal</xliff:g></item> - <item><xliff:g id="id">data_connection</xliff:g></item> - <item><xliff:g id="id">cdma_eri</xliff:g></item> - <item><xliff:g id="id">tty</xliff:g></item> - <item><xliff:g id="id">volume</xliff:g></item> - <item><xliff:g id="id">mute</xliff:g></item> - <item><xliff:g id="id">speakerphone</xliff:g></item> - <item><xliff:g id="id">wifi</xliff:g></item> - <item><xliff:g id="id">tty</xliff:g></item> - <item><xliff:g id="id">bluetooth</xliff:g></item> - <item><xliff:g id="id">gps</xliff:g></item> - <item><xliff:g id="id">sync_active</xliff:g></item> - <item><xliff:g id="id">sync_failing</xliff:g></item> - <item><xliff:g id="id">ime</xliff:g></item> - </string-array> - </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 6d6c47f..8d4fa4e 100644..100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -916,6 +916,10 @@ <enum name="KEYCODE_MEDIA_REWIND" value="89" /> <enum name="KEYCODE_MEDIA_FAST_FORWARD" value="90" /> <enum name="KEYCODE_MUTE" value="91" /> + <enum name="KEYCODE_PAGE_UP" value="92" /> + <enum name="KEYCODE_PAGE_DOWN" value="93" /> + <enum name="KEYCODE_PICTSYMBOLS" value="94" /> + <enum name="KEYCODE_SWITCH_CHARSET" value="95" /> </attr> <!-- ***************************************************************** --> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index ed7447c..5ca7b28 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -62,6 +62,22 @@ a reference to a Drawable resource containing the image definition. --> <attr name="icon" format="reference" /> + <!-- A Drawable resource providing an extended graphical logo for its + associated item. Use with the application tag (to supply a default + logo for all application components), or with the activity, receiver, + service, or instrumentation tag (to supply a specific logo for that + component). It may also be used with the intent-filter tag to supply + a logo to show to the user when an activity is being selected based + on a particular Intent. + + <p>The given logo will be used to display to the user a graphical + representation of its associated component; for example as the + header in the Action Bar. The primary differences between an icon + and a logo are that logos are often wider and more detailed, and are + used without an accompanying text caption. This must be a reference + to a Drawable resource containing the image definition. --> + <attr name="logo" format="reference" /> + <!-- Name of the activity to be launched to manage application's space on device. The specified activity gets automatically launched when the application's space needs to be managed and is usually invoked @@ -79,6 +95,13 @@ by applications. --> <attr name="allowClearUserData" format="boolean" /> + <!-- Option to let applications specify that user data should + never be encrypted if an Encrypted File System solution + is enabled. Specifically, this is an "opt-out" feature, meaning + that, by default, user data will be encrypted if the EFS feature + is enabled. --> + <attr name="neverEncrypt" format="boolean" /> + <!-- Option to indicate this application is only for testing purposes. For example, it may expose functionality or data outside of itself that would cause a security hole, but is useful for testing. This @@ -688,6 +711,7 @@ <attr name="theme" /> <attr name="label" /> <attr name="icon" /> + <attr name="logo" /> <attr name="description" /> <attr name="permission" /> <attr name="process" /> @@ -715,6 +739,16 @@ <attr name="killAfterRestore" /> <attr name="restoreNeedsApplication" /> <attr name="restoreAnyVersion" /> + <attr name="neverEncrypt" /> + <!-- Declare that this is a heavy-weight application. This kind of + application is not able to save and restore its state on demand, + so can not participate in the normal activity lifecycle. It will + not be killed while in the background; the user must explicitly + quit it. Only one such app can be running at a time; if the user + tries to launch a second heavy-weight app, they will be prompted + to quit the first before doing so. While a heavy-weight + application is running, the user will be informed of this. --> + <attr name="heavyWeight" format="boolean" /> </declare-styleable> <!-- The <code>permission</code> tag declares a security permission that can be @@ -734,6 +768,7 @@ <attr name="name" /> <attr name="label" /> <attr name="icon" /> + <attr name="logo" /> <attr name="permissionGroup" /> <attr name="description" /> <attr name="protectionLevel" /> @@ -758,6 +793,7 @@ <attr name="name" /> <attr name="label" /> <attr name="icon" /> + <attr name="logo" /> <attr name="description" /> </declare-styleable> @@ -787,6 +823,7 @@ <attr name="name" /> <attr name="label" /> <attr name="icon" /> + <attr name="logo" /> </declare-styleable> <!-- The <code>uses-permission</code> tag requests a @@ -937,6 +974,8 @@ screen, so that it retains the dimensions it was originally designed for. --> <attr name="largeScreens" format="boolean" /> + <!-- Indicates whether the application supports extra large screen form-factors. --> + <attr name="xlargeScreens" format="boolean" /> <!-- Indicates whether the application can resize itself to newer screen sizes. This is mostly used to distinguish between old applications that may not be compatible with newly introduced @@ -989,6 +1028,7 @@ <attr name="label" /> <attr name="description" /> <attr name="icon" /> + <attr name="logo" /> <attr name="process" /> <attr name="authorities" /> <attr name="syncable" /> @@ -1068,6 +1108,7 @@ <attr name="label" /> <attr name="description" /> <attr name="icon" /> + <attr name="logo" /> <attr name="permission" /> <attr name="process" /> <!-- Specify whether the service is enabled or not (that is, can be instantiated by the system). @@ -1100,6 +1141,7 @@ <attr name="label" /> <attr name="description" /> <attr name="icon" /> + <attr name="logo" /> <attr name="permission" /> <attr name="process" /> <!-- Specify whether the receiver is enabled or not (that is, can be instantiated by the system). @@ -1132,6 +1174,7 @@ <attr name="label" /> <attr name="description" /> <attr name="icon" /> + <attr name="logo" /> <attr name="launchMode" /> <attr name="screenOrientation" /> <attr name="configChanges" /> @@ -1185,6 +1228,7 @@ <attr name="label" /> <attr name="description" /> <attr name="icon" /> + <attr name="logo" /> <attr name="permission" /> <!-- Specify whether the activity-alias is enabled or not (that is, can be instantiated by the system). It can also be specified for an application as a whole, in which case a value of "false" @@ -1254,6 +1298,7 @@ parent="AndroidManifestActivity AndroidManifestReceiver AndroidManifestService"> <attr name="label" /> <attr name="icon" /> + <attr name="logo" /> <attr name="priority" /> </declare-styleable> @@ -1360,6 +1405,7 @@ <attr name="targetPackage" /> <attr name="label" /> <attr name="icon" /> + <attr name="logo" /> <attr name="handleProfiling" /> <attr name="functionalTest" /> </declare-styleable> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 088ab44..cffcd1d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -19,7 +19,35 @@ <!-- These resources are around just to allow their values to be customized for different hardware and product builds. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Component to be used as the status bar service. Must implement the IStatusBar + interface. This name is in the ComponentName flattened format (package/class) --> + <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.PhoneStatusBarService</string> + + <!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the + icons in the status bar that are not notifications. --> + <string-array name="config_statusBarIcons"> + <item><xliff:g id="id">ime</xliff:g></item> + <item><xliff:g id="id">sync_failing</xliff:g></item> + <item><xliff:g id="id">sync_active</xliff:g></item> + <item><xliff:g id="id">gps</xliff:g></item> + <item><xliff:g id="id">bluetooth</xliff:g></item> + <item><xliff:g id="id">tty</xliff:g></item> + <item><xliff:g id="id">speakerphone</xliff:g></item> + <item><xliff:g id="id">mute</xliff:g></item> + <item><xliff:g id="id">volume</xliff:g></item> + <item><xliff:g id="id">tty</xliff:g></item> + <item><xliff:g id="id">wifi</xliff:g></item> + <item><xliff:g id="id">cdma_eri</xliff:g></item> + <item><xliff:g id="id">data_connection</xliff:g></item> + <item><xliff:g id="id">phone_evdo_signal</xliff:g></item> + <item><xliff:g id="id">phone_signal</xliff:g></item> + <item><xliff:g id="id">battery</xliff:g></item> + <item><xliff:g id="id">alarm_clock</xliff:g></item> + <item><xliff:g id="id">secure</xliff:g></item> + <item><xliff:g id="id">clock</xliff:g></item> + </string-array> + <!-- Flag indicating whether the surface flinger has limited alpha compositing functionality in hardware. If set, the window manager will disable alpha trasformation in animations where not @@ -230,9 +258,6 @@ <!-- Allow the menu hard key to be disabled in LockScreen on some devices --> <bool name="config_disableMenuKeyInLockScreen">false</bool> - <!-- Control whether status bar should distinguish HSPA data icon form UMTS data icon on devices --> - <bool name="config_hspa_data_distinguishable">false</bool> - <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support. The N entries of this array define N + 1 zones as follows: diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 98c3a0a..1932771 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1239,4 +1239,24 @@ <public type="anim" name="cycle_interpolator" id="0x010a000c" /> +<!-- =============================================================== + Resources introduced in kraken. + =============================================================== --> + + <public type="attr" name="logo" id="0x010102be" /> + <public type="attr" name="xlargeScreens" id="0x010102bf" /> + <public type="attr" name="heavyWeight" id="0x010102c0" /> + <public-padding type="attr" name="kraken_resource_pad" end="0x01010300" /> + + <public-padding type="id" name="kraken_resource_pad" end="0x01020040" /> + <public-padding type="anim" name="kraken_resource_pad" end="0x010a0020" /> + <public-padding type="drawable" name="kraken_resource_pad" end="0x01080100" /> + <public-padding type="style" name="kraken_resource_pad" end="0x01030090" /> + <public-padding type="string" name="kraken_resource_pad" end="0x01040020" /> + <public-padding type="integer" name="kraken_resource_pad" end="0x010e0010" /> + <public-padding type="layout" name="kraken_resource_pad" end="0x01090020" /> + <public-padding type="dimen" name="kraken_resource_pad" end="0x01050010" /> + <public-padding type="color" name="kraken_resource_pad" end="0x01060020" /> + <public-padding type="array" name="kraken_resource_pad" end="0x01070010" /> + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 86bfe94..fc1db18 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -60,6 +60,10 @@ <!-- Displayed when the user dialed an MMI code whose function could not be performed. This will be displayed in a toast. --> <string name="mmiError">Connection problem or invalid MMI code.</string> + <!-- Displayed when the user dialed an MMI code whose function + could not be performed because FDN is enabled. This will be displayed in a toast. --> + <string name="mmiFdnError">Operation is restricted to fixed dialing numbers only.</string> + <!-- Displayed when a phone feature such as call barring was activated. --> <string name="serviceEnabled">Service was enabled.</string> <!-- Displayed in front of the list of a set of service classes @@ -389,6 +393,11 @@ the status bar or add and remove system icons.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_statusBarService">status bar</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_statusBarService">Allows the application to be the status bar.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_expandStatusBar">expand/collapse status bar</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_expandStatusBar">Allows application to @@ -1563,45 +1572,6 @@ <!-- A format string for 12-hour time of day, just the hour, not the minute, with capital "AM" or "PM" (example: "3PM"). --> <string name="hour_cap_ampm">"<xliff:g id="hour" example="3">%-l</xliff:g><xliff:g id="ampm" example="PM">%p</xliff:g>"</string> - <!-- The text for the button in the notification window-shade that clears - all of the currently visible notifications. --> - <string name="status_bar_clear_all_button">Clear</string> - - <!-- The label in the bar at the top of the status bar when there are no notifications - showing. --> - <string name="status_bar_no_notifications_title">No notifications</string> - - <!-- The label for the group of notifications for ongoing events in the opened version of - the status bar. An ongoing call is the prime example of this. The MP3 music player - might be another example. --> - <string name="status_bar_ongoing_events_title">Ongoing</string> - - <!-- The label for the group of notifications for recent events in the opened version of - the status bar. Recently received text messsages (SMS), emails, calendar alerts, etc. --> - <string name="status_bar_latest_events_title">Notifications</string> - - <!-- The big percent text in the middle of the battery icon that appears when you plug in - the charger. --> - <string name="battery_status_text_percent_format"><xliff:g id="number" example="50">%d</xliff:g><xliff:g id="percent" example="%">%%</xliff:g></string> - - <!-- The big percent text in the middle of the battery icon that appears when you plug in - the charger. This indicates the current status of the battery. --> - <string name="battery_status_charging">Charging\u2026</string> - - <!-- When the battery is low, this is displayed to the user in a dialog. The title of the low battery alert. --> - <string name="battery_low_title">Please connect charger</string> - - <!-- When the battery is low, this is displayed to the user in a dialog. The subtitle of the low battery alert. --> - <string name="battery_low_subtitle">The battery is getting low:</string> - - <!-- A message that appears when the battery level is getting low in a dialog. This is appened to the subtitle of the low battery alert. --> - <string name="battery_low_percent_format"><xliff:g id="number">%d%%</xliff:g> - or less remaining.</string> - - <!-- When the battery is low, this is the label of the button to go to the - power usage activity to find out what drained the battery. --> - <string name="battery_low_why">Battery use</string> - <!-- Title of the alert when something went wrong in the factory test. --> <string name="factorytest_failed">Factory test failed</string> <!-- Error message displayed when a non-system application tries to start a factory test. --> @@ -1970,8 +1940,29 @@ <!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. --> <string name="wait">Wait</string> + <!-- Notification text to tell the user that a heavy-weight application is running. --> + <string name="heavy_weight_notification"><xliff:g id="app">%1$s</xliff:g> running</string> + + <!-- Notification details to tell the user that a heavy-weight application is running. --> + <string name="heavy_weight_notification_detail">Select to switch to application</string> + + <!-- Title of dialog prompting whether user wants to switch between heavy-weight apps. --> + <string name="heavy_weight_switcher_title">Switch applications?</string> + + <!-- Descriptive text for switching to a new heavy-weight application. --> + <string name="heavy_weight_switcher_text">Another application is already running + that must be stopped before you can start a new one.</string> + + <string name="old_app_action">Return to <xliff:g id="old_app">%1$s</xliff:g></string> + <string name="old_app_description">Don\'t start the new application.</string> + + <string name="new_app_action">Start <xliff:g id="old_app">%1$s</xliff:g></string> + <string name="new_app_description">Stop the old application without saving.</string> + <!-- Displayed in the title of the chooser for things to do with text that - is to be sent to another application. For example, I can send text through SMS or IM. A dialog with those choices would be shown, and this would be the title. --> + is to be sent to another application. For example, I can send + text through SMS or IM. A dialog with those choices would be shown, + and this would be the title. --> <string name="sendText">Select an action for text</string> <!-- Title of the dialog where the user is adjusting the phone ringer volume --> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index b5fff96..af04117 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -187,11 +187,26 @@ <!-- Status Bar Styles --> - <style name="TextAppearance.StatusBarTitle"> + <style name="TextAppearance.StatusBar"> <item name="android:textAppearance">?android:attr/textAppearanceSmall</item> - <item name="android:textStyle">bold</item> <item name="android:textColor">?android:attr/textColorPrimary</item> </style> + <style name="TextAppearance.StatusBar.Ticker"> + </style> + <style name="TextAppearance.StatusBar.Title"> + <item name="android:textStyle">bold</item> + </style> + + <style name="TextAppearance.StatusBar.Icon"> + <item name="android:textStyle">bold</item> + </style> + <style name="TextAppearance.StatusBar.EventContent"> + <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> + </style> + <style name="TextAppearance.StatusBar.EventContent.Title"> + <item name="android:textSize">18sp</item> + <item name="android:textStyle">bold</item> + </style> <!-- Widget Styles --> diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml index c318577..5480993 100644 --- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml +++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml @@ -24,17 +24,19 @@ <application> <uses-library android:name="android.test.runner" /> <activity android:name="ConnectivityManagerTestActivity" - android:label="CMTest"> + android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.TEST" /> + <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <!-- This declares that this app uses the instrumentation test runner targeting - the package of browserpowertest. To run the tests use the command: - "adb shell am instrument -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner" + the package of connectivitymanagertest. To run the tests use the command: + "adb shell am instrument -e ssid <SSID> -w + com.android.connectivitymanagertest/.ConnectivityManagerTestRunner", + the access point <SSID> should be an open AP. --> <instrumentation android:name=".ConnectivityManagerTestRunner" android:targetPackage="com.android.connectivitymanagertest" diff --git a/core/tests/ConnectivityManagerTest/res/values/strings.xml b/core/tests/ConnectivityManagerTest/res/values/strings.xml new file mode 100644 index 0000000..fb6e82f --- /dev/null +++ b/core/tests/ConnectivityManagerTest/res/values/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">ConnectivityManagerTest</string> +</resources> diff --git a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java index 89b3fb6..639372b2 100644 --- a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java +++ b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java @@ -74,15 +74,15 @@ public class HierarchicalStateMachineTest extends TestCase { if (isQuit(message)) { mQuitCount += 1; if (mQuitCount > 2) { - // Returning false to actually quit - return false; + // Returning NOT_HANDLED to actually quit + return NOT_HANDLED; } else { // Do NOT quit - return true; + return HANDLED; } } else { // All other message are handled - return true; + return HANDLED; } } } @@ -172,12 +172,18 @@ public class HierarchicalStateMachineTest extends TestCase { class S1 extends HierarchicalState { @Override protected void enter() { + // Test that message is HSM_INIT_CMD + assertEquals(HSM_INIT_CMD, getCurrentMessage().what); + // Test that a transition in enter and the initial state works mS1EnterCount += 1; transitionTo(mS2); Log.d(TAG, "S1.enter"); } @Override protected void exit() { + // Test that message is HSM_INIT_CMD + assertEquals(HSM_INIT_CMD, getCurrentMessage().what); + mS1ExitCount += 1; Log.d(TAG, "S1.exit"); } @@ -185,10 +191,16 @@ public class HierarchicalStateMachineTest extends TestCase { class S2 extends HierarchicalState { @Override protected void enter() { + // Test that message is HSM_INIT_CMD + assertEquals(HSM_INIT_CMD, getCurrentMessage().what); + mS2EnterCount += 1; Log.d(TAG, "S2.enter"); } @Override protected void exit() { + // Test that message is TEST_CMD_1 + assertEquals(TEST_CMD_1, getCurrentMessage().what); + // Test transition in exit work mS2ExitCount += 1; transitionTo(mS4); @@ -196,10 +208,10 @@ public class HierarchicalStateMachineTest extends TestCase { } @Override protected boolean processMessage(Message message) { // Start a transition to S3 but it will be - // changed to a transition to S4 + // changed to a transition to S4 in exit transitionTo(mS3); Log.d(TAG, "S2.processMessage"); - return true; + return HANDLED; } } @@ -264,7 +276,7 @@ public class HierarchicalStateMachineTest extends TestCase { } synchronized (smEnterExitTranstionToTest) { - smEnterExitTranstionToTest.sendMessage(1); + smEnterExitTranstionToTest.sendMessage(TEST_CMD_1); try { // wait for the messages to be handled @@ -321,7 +333,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_6) { transitionToHaltingState(); } - return true; + return HANDLED; } } @@ -415,7 +427,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mExitCount); transitionToHaltingState(); } - return true; + return HANDLED; } @Override protected void exit() { @@ -510,7 +522,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionTo(mS2); } - return true; + return HANDLED; } @Override protected void exit() { @@ -523,7 +535,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } - return true; + return HANDLED; } } @@ -612,13 +624,13 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } - return true; + return HANDLED; } } class ChildState extends HierarchicalState { @Override protected boolean processMessage(Message message) { - return false; + return NOT_HANDLED; } } @@ -697,20 +709,20 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } - return true; + return HANDLED; } } class ChildState1 extends HierarchicalState { @Override protected boolean processMessage(Message message) { transitionTo(mChildState2); - return true; + return HANDLED; } } class ChildState2 extends HierarchicalState { @Override protected boolean processMessage(Message message) { - return false; + return NOT_HANDLED; } } @@ -794,7 +806,7 @@ public class HierarchicalStateMachineTest extends TestCase { mParentState1EnterCount += 1; } @Override protected boolean processMessage(Message message) { - return true; + return HANDLED; } @Override protected void exit() { mParentState1ExitCount += 1; @@ -822,7 +834,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState2); - return true; + return HANDLED; } @Override protected void exit() { mChildState1ExitCount += 1; @@ -850,7 +862,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState5); - return true; + return HANDLED; } @Override protected void exit() { mChildState2ExitCount += 1; @@ -878,7 +890,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionToHaltingState(); - return true; + return HANDLED; } @Override protected void exit() { mParentState2ExitCount += 1; @@ -906,7 +918,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionTo(mChildState4); - return true; + return HANDLED; } @Override protected void exit() { mChildState3ExitCount += 1; @@ -934,7 +946,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionTo(mParentState2); - return true; + return HANDLED; } @Override protected void exit() { mChildState4ExitCount += 1; @@ -962,7 +974,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState3); - return true; + return HANDLED; } @Override protected void exit() { mChildState5ExitCount += 1; @@ -1108,7 +1120,7 @@ public class HierarchicalStateMachineTest extends TestCase { mArrivalTimeMsg2 = SystemClock.elapsedRealtime(); transitionToHaltingState(); } - return true; + return HANDLED; } @Override protected void exit() { @@ -1190,7 +1202,7 @@ public class HierarchicalStateMachineTest extends TestCase { class S1 extends HierarchicalState { @Override protected boolean processMessage(Message message) { transitionTo(mS2); - return true; + return HANDLED; } @Override protected void exit() { sendMessage(TEST_CMD_2); @@ -1216,7 +1228,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (mMsgCount == 2) { transitionToHaltingState(); } - return true; + return HANDLED; } @Override protected void exit() { @@ -1300,7 +1312,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } - return false; + return NOT_HANDLED; } } @@ -1369,7 +1381,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_4) { transitionToHaltingState(); } - return true; + return HANDLED; } } @@ -1563,10 +1575,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 protected void exit() { @@ -1618,7 +1630,7 @@ class Hsm1 extends HierarchicalStateMachine { transitionToHaltingState(); break; } - return true; + return HANDLED; } @Override protected void exit() { Log.d(TAG, "P2.exit"); diff --git a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java index 004a197..2de0464 100644 --- a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java +++ b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java @@ -275,6 +275,16 @@ public class VCardExporterTests extends VCardTestsBase { testPhoneBasicCommon(V30); } + public void testPhoneRefrainFormatting() { + mVerifier.initForExportTest(V21 | VCardConfig.FLAG_REFRAIN_PHONE_NUMBER_FORMATTING); + mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "1234567890(abcdefghijklmnopqrstuvwxyz)") + .put(Phone.TYPE, Phone.TYPE_HOME); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "1234567890(abcdefghijklmnopqrstuvwxyz)", + new TypeSet("HOME")); + } + /** * Tests that vCard composer emits corresponding type param which we expect. */ diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java index 5b427be..a5229cc 100644 --- a/core/tests/coretests/src/android/text/TextUtilsTest.java +++ b/core/tests/coretests/src/android/text/TextUtilsTest.java @@ -26,6 +26,8 @@ import android.text.SpannedString; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.StyleSpan; +import android.text.util.Rfc822Token; +import android.text.util.Rfc822Tokenizer; import android.test.MoreAsserts; import com.android.common.Rfc822Validator; @@ -269,6 +271,24 @@ public class TextUtilsTest extends TestCase { } } + @SmallTest + public void testRfc822TokenizerFullAddress() { + Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("Foo Bar (something) <foo@google.com>"); + assertNotNull(tokens); + assertEquals(1, tokens.length); + assertEquals("foo@google.com", tokens[0].getAddress()); + assertEquals("Foo Bar", tokens[0].getName()); + assertEquals("something",tokens[0].getComment()); + } + + @SmallTest + public void testRfc822TokenizeItemWithError() { + Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("\"Foo Bar\\"); + assertNotNull(tokens); + assertEquals(1, tokens.length); + assertEquals("Foo Bar", tokens[0].getAddress()); + } + @LargeTest public void testEllipsize() { CharSequence s1 = "The quick brown fox jumps over \u00FEhe lazy dog."; diff --git a/core/tests/coretests/src/android/view/VelocityTest.java b/core/tests/coretests/src/android/view/VelocityTest.java new file mode 100644 index 0000000..fb28e1e --- /dev/null +++ b/core/tests/coretests/src/android/view/VelocityTest.java @@ -0,0 +1,285 @@ +/* + * 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.frameworktest.view; + +import junit.framework.Assert; + +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.MediumTest; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +/** + * Exercises {@link android.view.VelocityTracker} to compute correct velocity.<br> + * To launch this test, use :<br> + * <code>./development/testrunner/runtest.py framework -c com.android.frameworktest.view.VelocityTest</code> + */ +public class VelocityTest extends InstrumentationTestCase { + + @MediumTest + public void testInitialCondiditions() { + VelocityTracker vt = VelocityTracker.obtain(); + assertNotNull(vt); + vt.recycle(); + } + + /** + * Test that {@link android.view.VelocityTracker}.clear() clears + * the previous values after a call to computeCurrentVelocity() + */ + @MediumTest + public void testClear() { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 300); + vt.computeCurrentVelocity(1); + assertFalse("Velocity should not be null", vt.getXVelocity() == 0.0f); + assertFalse("Velocity should not be null", vt.getYVelocity() == 0.0f); + vt.clear(); + vt.computeCurrentVelocity(1); + assertEquals(0.0f, vt.getXVelocity()); + assertEquals(0.0f, vt.getYVelocity()); + vt.recycle(); + } + + @MediumTest + public void testDragAcceleration () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 15, t, 400, new AccelerateInterpolator()); + vt.computeCurrentVelocity(1000); + assertGreater(250.0f, vt.getXVelocity()); + assertGreater(250.0f, vt.getYVelocity()); + vt.recycle(); + } + + @MediumTest + public void testDragDeceleration () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 15, t, 400, new DecelerateInterpolator()); + vt.computeCurrentVelocity(1000); + assertLower(250.0f, vt.getXVelocity()); + assertLower(250.0f, vt.getYVelocity()); + vt.recycle(); + } + + @MediumTest + public void testDragLinearHorizontal() { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + // 100px in 400ms => 250px/s + drag(vt, 100, 200, 200, 200, 15, t, 400); + vt.computeCurrentVelocity(1000); + assertEquals(0.0f, vt.getYVelocity()); + assertEqualFuzzy(250.0f, vt.getXVelocity(), 4f); + vt.recycle(); + } + + @MediumTest + public void testDragLinearVertical() { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + // 100px in 400ms => 250px/s + drag(vt, 200, 200, 100, 200, 15, t, 400); + vt.computeCurrentVelocity(1000); + assertEquals(0.0f, vt.getXVelocity()); + assertEqualFuzzy(250.0f, vt.getYVelocity(), 4f); + vt.recycle(); + } + + /** + * Test dragging with two points only + * (velocity must be an exact value) + */ + @MediumTest + public void testDragWith2Points () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + // 100px, 2 steps, 100ms => 1000px/s + drag(vt, 100, 200, 100, 200, 2, t, 100); + vt.computeCurrentVelocity(1000); + assertEquals(1000.0f, vt.getXVelocity()); + assertEquals(1000.0f, vt.getYVelocity()); + vt.recycle(); + } + + /** + * Velocity is independent of the number of points used during + * the same interval + */ + @MediumTest + public void testStabilityInNbPoints () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 400); // 10 steps over 400ms + vt.computeCurrentVelocity(1); + float firstX = vt.getXVelocity(); + float firstY = vt.getYVelocity(); + vt.clear(); + drag(vt, 100, 200, 100, 200, 20, t, 400); // 20 steps over 400ms + vt.computeCurrentVelocity(1); + float secondX = vt.getXVelocity(); + float secondY = vt.getYVelocity(); + assertEqualFuzzy(firstX, secondX, 0.1f); + assertEqualFuzzy(firstY, secondY, 0.1f); + vt.recycle(); + } + + /** + * Velocity is independent of the time when the events occurs, + * it only depends on delays between the events. + */ + @MediumTest + public void testStabilityInTime () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 400); + vt.computeCurrentVelocity(1); + float firstX = vt.getXVelocity(); + float firstY = vt.getYVelocity(); + vt.clear(); + drag(vt, 100, 200, 100, 200, 10, t + 3600*1000, 400); // on hour later + vt.computeCurrentVelocity(1); + float secondX = vt.getXVelocity(); + float secondY = vt.getYVelocity(); + assertEqualFuzzy(firstX, secondX, 0.1f); + assertEqualFuzzy(firstY, secondY, 0.1f); + vt.recycle(); + } + + /** + * Velocity is independent of the position of the events, + * it only depends on their relative distance. + */ + @MediumTest + public void testStabilityInSpace () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 400); + vt.computeCurrentVelocity(1); + float firstX = vt.getXVelocity(); + float firstY = vt.getYVelocity(); + vt.clear(); + drag(vt, 200, 300, 200, 300, 10, t, 400); // 100px further + vt.computeCurrentVelocity(1); + float secondX = vt.getXVelocity(); + float secondY = vt.getYVelocity(); + assertEqualFuzzy(firstX, secondX, 0.1f); + assertEqualFuzzy(firstY, secondY, 0.1f); + vt.recycle(); + } + + /** + * Test that calls to {@link android.view.VelocityTracker}.computeCurrentVelocity() + * will output same values when using the same data. + */ + @MediumTest + public void testStabilityOfComputation() { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 300); + vt.computeCurrentVelocity(1); + float firstX = vt.getXVelocity(); + float firstY = vt.getYVelocity(); + vt.computeCurrentVelocity(1); + float secondX = vt.getXVelocity(); + float secondY = vt.getYVelocity(); + assertEquals(firstX, secondX); + assertEquals(firstY, secondY); + vt.recycle(); + } + + /** + * Test the units parameter of {@link android.view.VelocityTracker}.computeCurrentVelocity() + */ + @MediumTest + public void testStabilityOfUnits() { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 300); + vt.computeCurrentVelocity(1); + float firstX = vt.getXVelocity(); + float firstY = vt.getYVelocity(); + vt.computeCurrentVelocity(1000); + float secondX = vt.getXVelocity(); + float secondY = vt.getYVelocity(); + assertEqualFuzzy(firstX, secondX / 1000.0f, 0.1f); + assertEqualFuzzy(firstY, secondY / 1000.0f, 0.1f); + vt.recycle(); + } + + /** + * Simulate a drag by giving directly MotionEvents to + * the VelocityTracker using a linear interpolator + */ + private void drag(VelocityTracker vt, int startX, int endX, int startY, int endY, int steps, + long startime, int duration) { + drag(vt, startX, endX, startY, endY, steps, startime, duration, new LinearInterpolator()); + } + + /** + * Simulate a drag by giving directly MotionEvents to + * the VelocityTracker using a given interpolator + */ + private void drag(VelocityTracker vt, int startX, int endX, int startY, int endY, int steps, + long startime, int duration, Interpolator interpolator) { + addMotionEvent(vt, startX, startY, startime, MotionEvent.ACTION_DOWN); + float dt = duration / (float)steps; + int distX = endX - startX; + int distY = endY - startY; + for (int i=1; i<steps-1; i++) { + float ii = interpolator.getInterpolation(i / (float)steps); + int x = (int) (startX + distX * ii); + int y = (int) (startY + distY * ii); + long time = startime + (int) (i * dt); + addMotionEvent(vt, x, y, time, MotionEvent.ACTION_MOVE); + } + addMotionEvent(vt, endX, endY, startime + duration, MotionEvent.ACTION_UP); + } + + private void addMotionEvent(VelocityTracker vt, int x, int y, long time, int action) { + MotionEvent me = MotionEvent.obtain(time, time, action, x, y, 0); + vt.addMovement(me); + me.recycle(); + } + + /** + * Float imprecision of the average computations and filtering + * (removing last MotionEvent for N > 3) implies that tests + * accepts some approximated values. + */ + private void assertEqualFuzzy(float expected, float actual, float threshold) { + boolean fuzzyEqual = actual >= expected - threshold && actual <= expected + threshold; + Assert.assertTrue("Expected: <"+expected+"> but was: <"+actual+ + "> while accepting a variation of: <"+threshold+">", fuzzyEqual); + } + + private void assertGreater(float minExpected, float actual) { + Assert.assertTrue("Expected: minimum <"+minExpected+"> but was: <"+actual+">", + actual > minExpected); + } + + private void assertLower(float maxExpected, float actual) { + Assert.assertTrue("Expected: maximum <"+maxExpected+"> but was: <"+actual+">", + actual < maxExpected); + } +} |